diff --git a/DEPS b/DEPS
index bdff792..5bd68b57 100644
--- a/DEPS
+++ b/DEPS
@@ -300,19 +300,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': 'bcd1edce7de23dda9e50fd7bf840b5438975b942',
+  'src_internal_revision': '93d8f1d5b956eb8dc7857ad3083c8afcdb8ab288',
   # 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': '44b4a40178cc671cf1576e4d5a20bc094dff0342',
+  'skia_revision': '79ea64d7a71871ca51e2037d5ece6847ec9211e7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'b5ecf9ac14f0683748aaa8f2bce6b239f434d9ae',
+  'v8_revision': '8fec6dbcd949d2b6b1d84ca3758cac44582968bd',
   # 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': '11c0a783110452d90c45f65c79196c1b0d096e6b',
+  'angle_revision': 'f0919be383d0d6ad76120fd7a3ed6b3beddb74d7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -320,11 +320,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'd5f3e3a18024afa0f6b05d8a7fe05086bdb377d8',
+  'pdfium_revision': 'fb7720b24edf6a31672298e181775f951ca3924e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': '40ec347196a939bd4fd1f801df896b2f4e2205dc',
+  'boringssl_revision': 'e724ef02089bf2bb494203231fc5cb62acc2fad6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
@@ -376,7 +376,7 @@
   # 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': '2d53ec23e6a27cba13b7695c0d73efe5dcaec1e7',
+  'chromium_variations_revision': '5a75b26ac7173f7a9b010b02a0cbbc60ef5c159c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -396,7 +396,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '535c69c69e1bcbcfc8f47dd93d74374abdbb80a1',
+  'devtools_frontend_revision': '4b11dfea9a3853a1da464c2e527408c7cd6091de',
   # 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.
@@ -1287,7 +1287,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'b0adcc518a532ef53db88a1239c211f4bd4d85e5',
+    '07fe1b4bc66d1a5407385d6699e86106cb551735',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1501,7 +1501,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/error_prone',
-               'version': 'iksKTcNa8fCfCXLvYa9Og9yhPWH8iTk7xbESPSw243QC',
+               'version': 'fNCLAzE8NSvOXTryvUGT3NmX8no8lyRHR1yfY0zbv8YC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1725,7 +1725,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' + '@' + 'c65df07901b4f506da77443659bcf0319b70e8cd',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c84bf5c21fa3c0340441768c1feeacc96cf46497',
       'condition': 'checkout_chromeos',
   },
 
@@ -1760,13 +1760,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1ad5b6c0df87d570420c9f833c0c024fa863853b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '17226d7965e188c68162b4765ffaa24ec8883f52',
 
   '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' + '@' + 'd28a0b79790f235c4124e148509fff1a92bd832b',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '3a82558ffc48810616f624261d343591799d5730',
     'condition': 'checkout_src_internal',
   },
 
@@ -2252,7 +2252,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '76c9a3333b19b28f567ed19a22d2ae04e9663803',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '40477ffb51d78b2b35aa049bcae0c4f8928235e6',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2540,7 +2540,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@49bb428cd4514a34c4626a7589e2251d5b50dced',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@6526c75bbc69c0c529ee9005e0b2689cfc8d0722',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@12a17b7ce41436427e358608183100b1103274da',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@2a9b6f951c7d6b04b6c21fe1bf3f475b68b84801',
@@ -2549,7 +2549,7 @@
   'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@1108bba6c97174d172d45470a7470a3d6a564647',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@4c63e845962ff3b197855f3ae4907a47d0863f5a',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@ea5774a13e3017b6d5d79af6fba9f0d72ca5c61a',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@50910c05fdc909aba59cc71e6320b0c5908912cd',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@ef846ac0883cde5e69ced0e7d7af59fe92f34e25',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -2586,10 +2586,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '1b6371436a0a60e6b9a4ae2a40a8eba198e3af02',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9619e6a6ab523961fb5635ba9bc32d4a647f3074',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '32083568bca5204c5cd6baa32c2e0797ef148200',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '51a2bd130d96136638fc462f8c3d2ca5d915ac11',
+    Var('webrtc_git') + '/src.git' + '@' + '97d04270808f04250d36fdc8c72c386e793e4b7a',
 
   # 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.
@@ -2729,7 +2729,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'D4nkB5VFPG9-GLpCnVCrccNgMdDTNyHU4sqUnlUsWrMC',
+        'version': 'zo2Y3XzqKGW8NKpZ26DThmfyY9AqusT3ezdwuFxPhbIC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2740,7 +2740,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '4w4h06ntC2BWYvpyzRics6DPU1mHKfGFC4Wn2rhZWXEC',
+        'version': 'YvAOC7ooU4_DAXrG7n8SfnQrKpnfQguJ7nWtA8EmERUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3128,28 +3128,6 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/android_deps/cipd/libs/com_google_auto_auto_common': {
-      'packages': [
-          {
-              'package': 'chromium/third_party/android_deps/libs/com_google_auto_auto_common',
-              'version': 'version:2@1.2.1.cr1',
-          },
-      ],
-      'condition': 'checkout_android and non_git_source',
-      'dep_type': 'cipd',
-  },
-
-  'src/third_party/android_deps/cipd/libs/com_google_auto_service_auto_service': {
-      'packages': [
-          {
-              'package': 'chromium/third_party/android_deps/libs/com_google_auto_service_auto_service',
-              'version': 'version:2@1.0-rc6.cr1',
-          },
-      ],
-      'condition': 'checkout_android and non_git_source',
-      'dep_type': 'cipd',
-  },
-
   'src/third_party/android_deps/cipd/libs/com_google_auto_service_auto_service_annotations': {
       'packages': [
           {
@@ -4168,7 +4146,7 @@
 
   'src/chrome/browser/platform_experience/win': {
       'url': Var('chrome_git') + '/chrome/browser/platform_experience/win.git' + '@' +
-        'f6d1522a6859c6db431fa7581179ed9342ddc457',
+        '2852ae88cdeb024e620598dce0a3aab80ab7d5b6',
       'condition': 'checkout_src_internal',
   },
 
@@ -4322,7 +4300,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        'e3a75e7f401aaa2cf290716560fd066072fb2772',
+        'ffacd3be35c88f3600039204210e14e31d1a04cc',
       'condition': 'checkout_src_internal',
   },
 
@@ -4382,7 +4360,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '7a2df944059b4837fd4c2cfabe6b251b3c8c6b47',
+        '4c39224d86d58e8c00816ac9a937e81aba8a8e1b',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/common/crash_reporter/crash_keys.cc b/android_webview/common/crash_reporter/crash_keys.cc
index 3122cb3..c2bbe3a 100644
--- a/android_webview/common/crash_reporter/crash_keys.cc
+++ b/android_webview/common/crash_reporter/crash_keys.cc
@@ -178,6 +178,26 @@
     "switch-*",
     "num-switches",
 
+    // NavigationListener investigation
+    "NoTrackedNav-message",
+    "NoTrackedNav-nav_id",
+    "NoTrackedNav-url_type",
+    "NoTrackedNav-prev_url_type",
+
+    "NoTrackedNav-discard_reason",
+    "NoTrackedNav-tracked_navs_size",
+    "NoTrackedNav-all_navs_size",
+    "NoTrackedNav-net_error_code",
+
+    "NoTrackedNav-has_committed",
+    "NoTrackedNav-was_redirect",
+    "NoTrackedNav-is_activation",
+    "NoTrackedNav-is_same_doc",
+    "NoTrackedNav-is_renderer",
+    "NoTrackedNav-is_reload",
+    "NoTrackedNav-is_history",
+    "NoTrackedNav-is_restore",
+
     nullptr};
 // clang-format on
 
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 9a052e5..23da1844 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -1056,6 +1056,7 @@
                 AwFeatures.WEBVIEW_DIGITAL_ASSET_LINKS_LOAD_INCLUDES,
                 "Enable loading include statements when checking digital asset links."),
         Flag.baseFeature("PrefetchNewWaitLoop"),
+        Flag.baseFeature("DirectCompositorThreadIpc"),
 
         // Add new commandline switches and features above. The final entry should have a
         // trailing comma for cleaner diffs.
diff --git a/android_webview/renderer/BUILD.gn b/android_webview/renderer/BUILD.gn
index 87a1038..6d2b8a41 100644
--- a/android_webview/renderer/BUILD.gn
+++ b/android_webview/renderer/BUILD.gn
@@ -18,8 +18,6 @@
     "aw_render_thread_observer.h",
     "aw_render_view_ext.cc",
     "aw_render_view_ext.h",
-    "aw_safe_browsing_error_page_controller_delegate_impl.cc",
-    "aw_safe_browsing_error_page_controller_delegate_impl.h",
     "aw_url_loader_throttle_provider.cc",
     "aw_url_loader_throttle_provider.h",
     "browser_exposed_renderer_interfaces.cc",
@@ -44,7 +42,7 @@
     "//components/safe_browsing/content/common:interfaces",
     "//components/safe_browsing/content/renderer:throttles",
     "//components/safe_browsing/core/common",
-    "//components/security_interstitials/content/renderer:security_interstitial_page_controller",
+    "//components/security_interstitials/content/renderer",
     "//components/security_interstitials/core",
     "//components/security_interstitials/core/common/mojom",
     "//components/spellcheck:buildflags",
diff --git a/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc
index eab931eb..68d15f9 100644
--- a/android_webview/renderer/aw_content_renderer_client.cc
+++ b/android_webview/renderer/aw_content_renderer_client.cc
@@ -16,7 +16,6 @@
 #include "android_webview/renderer/aw_print_render_frame_helper_delegate.h"
 #include "android_webview/renderer/aw_render_frame_ext.h"
 #include "android_webview/renderer/aw_render_view_ext.h"
-#include "android_webview/renderer/aw_safe_browsing_error_page_controller_delegate_impl.h"
 #include "android_webview/renderer/aw_url_loader_throttle_provider.h"
 #include "android_webview/renderer/browser_exposed_renderer_interfaces.h"
 #include "base/command_line.h"
@@ -30,6 +29,7 @@
 #include "components/network_hints/renderer/web_prescient_networking_impl.h"
 #include "components/page_load_metrics/renderer/metrics_render_frame_observer.h"
 #include "components/printing/renderer/print_render_frame_helper.h"
+#include "components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.h"
 #include "components/visitedlink/renderer/visitedlink_reader.h"
 #include "content/public/child/child_thread.h"
 #include "content/public/common/url_constants.h"
@@ -155,7 +155,8 @@
       render_frame, std::make_unique<AwPrintRenderFrameHelperDelegate>());
   new AwRenderFrameExt(render_frame);
   new js_injection::JsCommunication(render_frame);
-  new AwSafeBrowsingErrorPageControllerDelegateImpl(render_frame);
+  new security_interstitials::SecurityInterstitialPageControllerDelegateImpl(
+      render_frame);
 
   content::RenderFrame* main_frame = render_frame->GetMainRenderFrame();
   if (main_frame && main_frame != render_frame) {
@@ -211,7 +212,8 @@
     content::mojom::AlternativeErrorPageOverrideInfoPtr
         alternative_error_page_info,
     std::string* error_html) {
-  AwSafeBrowsingErrorPageControllerDelegateImpl::Get(render_frame)
+  security_interstitials::SecurityInterstitialPageControllerDelegateImpl::Get(
+      render_frame)
       ->PrepareForErrorPage();
 
   android_system_error_page::PopulateErrorPageHtml(error, error_html);
diff --git a/android_webview/renderer/aw_safe_browsing_error_page_controller_delegate_impl.cc b/android_webview/renderer/aw_safe_browsing_error_page_controller_delegate_impl.cc
deleted file mode 100644
index f495177..0000000
--- a/android_webview/renderer/aw_safe_browsing_error_page_controller_delegate_impl.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "android_webview/renderer/aw_safe_browsing_error_page_controller_delegate_impl.h"
-
-#include "content/public/renderer/render_frame.h"
-
-namespace android_webview {
-
-AwSafeBrowsingErrorPageControllerDelegateImpl::
-    AwSafeBrowsingErrorPageControllerDelegateImpl(
-        content::RenderFrame* render_frame)
-    : content::RenderFrameObserver(render_frame),
-      content::RenderFrameObserverTracker<
-          AwSafeBrowsingErrorPageControllerDelegateImpl>(render_frame) {}
-
-AwSafeBrowsingErrorPageControllerDelegateImpl::
-    ~AwSafeBrowsingErrorPageControllerDelegateImpl() = default;
-
-void AwSafeBrowsingErrorPageControllerDelegateImpl::PrepareForErrorPage() {
-  pending_error_ = true;
-}
-
-void AwSafeBrowsingErrorPageControllerDelegateImpl::OnDestruct() {
-  delete this;
-}
-
-void AwSafeBrowsingErrorPageControllerDelegateImpl::DidCommitProvisionalLoad(
-    ui::PageTransition transition) {
-  committed_error_ = pending_error_;
-  pending_error_ = false;
-}
-
-void AwSafeBrowsingErrorPageControllerDelegateImpl::DidFinishLoad() {
-  if (committed_error_) {
-    security_interstitials::SecurityInterstitialPageController::Install(
-        render_frame());
-  }
-}
-
-}  // namespace android_webview
diff --git a/android_webview/renderer/aw_safe_browsing_error_page_controller_delegate_impl.h b/android_webview/renderer/aw_safe_browsing_error_page_controller_delegate_impl.h
deleted file mode 100644
index 0117af6..0000000
--- a/android_webview/renderer/aw_safe_browsing_error_page_controller_delegate_impl.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ANDROID_WEBVIEW_RENDERER_AW_SAFE_BROWSING_ERROR_PAGE_CONTROLLER_DELEGATE_IMPL_H_
-#define ANDROID_WEBVIEW_RENDERER_AW_SAFE_BROWSING_ERROR_PAGE_CONTROLLER_DELEGATE_IMPL_H_
-
-#include "components/security_interstitials/content/renderer/security_interstitial_page_controller.h"
-#include "components/security_interstitials/core/controller_client.h"
-#include "content/public/renderer/render_frame_observer.h"
-#include "content/public/renderer/render_frame_observer_tracker.h"
-
-namespace content {
-class RenderFrame;
-}  // namespace content
-
-namespace android_webview {
-
-class AwSafeBrowsingErrorPageControllerDelegateImpl
-    : public content::RenderFrameObserver,
-      public content::RenderFrameObserverTracker<
-          AwSafeBrowsingErrorPageControllerDelegateImpl> {
- public:
-  explicit AwSafeBrowsingErrorPageControllerDelegateImpl(
-      content::RenderFrame* render_frame);
-
-  // Disallow copy and assign
-  AwSafeBrowsingErrorPageControllerDelegateImpl(
-      const AwSafeBrowsingErrorPageControllerDelegateImpl&) = delete;
-  AwSafeBrowsingErrorPageControllerDelegateImpl& operator=(
-      const AwSafeBrowsingErrorPageControllerDelegateImpl&) = delete;
-
-  ~AwSafeBrowsingErrorPageControllerDelegateImpl() override;
-
-  // Notifies us that a navigation error has occurred and will be committed
-  void PrepareForErrorPage();
-
-  // content::RenderFrameObserver:
-  void OnDestruct() override;
-  void DidCommitProvisionalLoad(ui::PageTransition transition) override;
-  void DidFinishLoad() override;
-
- private:
-  // Whether there is an error page pending to be committed.
-  bool pending_error_ = false;
-
-  // Whether the committed page is an error page.
-  bool committed_error_ = false;
-};
-
-}  // namespace android_webview
-
-#endif  // ANDROID_WEBVIEW_RENDERER_AW_SAFE_BROWSING_ERROR_PAGE_CONTROLLER_DELEGATE_IMPL_H_
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index 3e77b52..113288e 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -2899,6 +2899,11 @@
       static_cast<int>(DisableTrackpadMode::kNever));
 }
 
+DisableTrackpadMode AccessibilityController::GetDisableTrackpadMode() {
+  return static_cast<DisableTrackpadMode>(
+      active_user_prefs_->GetInteger(prefs::kAccessibilityDisableTrackpadMode));
+}
+
 void AccessibilityController::UpdateColorCorrectionFromPrefs() {
   DCHECK(active_user_prefs_);
 
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index 9d6fde3..9a5ddf1 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -62,6 +62,7 @@
 enum class DictationBubbleIconType;
 enum class DictationNotificationType;
 class DisableTrackpadEventRewriter;
+enum class DisableTrackpadMode;
 class FilterKeysEventRewriter;
 class FlashScreenController;
 class FloatingAccessibilityController;
@@ -355,6 +356,7 @@
   void SetAccessibilityEventRewriter(
       AccessibilityEventRewriter* accessibility_event_rewriter);
   void SetDisableTrackpadEventRewriter(DisableTrackpadEventRewriter* rewriter);
+  DisableTrackpadMode GetDisableTrackpadMode();
   void SetFilterKeysEventRewriter(FilterKeysEventRewriter* rewriter);
   bool IsPointScanEnabled();
 
diff --git a/ash/accessibility/disable_trackpad_event_rewriter.cc b/ash/accessibility/disable_trackpad_event_rewriter.cc
index a7ef34f9..4f21088 100644
--- a/ash/accessibility/disable_trackpad_event_rewriter.cc
+++ b/ash/accessibility/disable_trackpad_event_rewriter.cc
@@ -5,14 +5,73 @@
 #include "ash/accessibility/disable_trackpad_event_rewriter.h"
 
 #include "ash/accessibility/accessibility_controller.h"
+#include "ash/public/cpp/accessibility_controller_enums.h"
+#include "ash/public/mojom/input_device_settings.mojom.h"
 #include "ash/shell.h"
+#include "ash/system/input_device_settings/input_device_settings_controller_impl.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/event.h"
 
 namespace ash {
 
 namespace {
-bool IsFromInternalTrackpad(const ui::Event& event) {
-  // TODO(b/354176487): Implement checking for internal trackpad.
-  return true;
+
+bool IsExternalDevice(const ui::InputDevice& device) {
+  return device.type == ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH ||
+         device.type == ui::InputDeviceType::INPUT_DEVICE_USB;
+}
+
+bool IsExternalMouseOrTrackpadConnected() {
+  ui::DeviceDataManager* device_data_manager =
+      ui::DeviceDataManager::GetInstance();
+  InputDeviceSettingsControllerImpl* input_device_settings_controller =
+      Shell::Get()->input_device_settings_controller();
+
+  // Check for external touchpads.
+  for (const auto& touchpad : device_data_manager->GetTouchpadDevices()) {
+    const mojom::Touchpad* found_device =
+        input_device_settings_controller->GetTouchpad(touchpad.id);
+
+    if (found_device != nullptr && IsExternalDevice(touchpad)) {
+      return true;
+    }
+  }
+
+  // Check for external mice.
+  for (const auto& mouse : device_data_manager->GetMouseDevices()) {
+    const mojom::Mouse* found_device =
+        input_device_settings_controller->GetMouse(mouse.id);
+
+    if (found_device != nullptr && IsExternalDevice(mouse)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+int GetInternalTrackpadDeviceId() {
+  ui::DeviceDataManager* device_data_manager =
+      ui::DeviceDataManager::GetInstance();
+  InputDeviceSettingsControllerImpl* input_device_settings_controller =
+      Shell::Get()->input_device_settings_controller();
+
+  for (const auto& touchpad : device_data_manager->GetTouchpadDevices()) {
+    const mojom::Touchpad* found_device =
+        input_device_settings_controller->GetTouchpad(touchpad.id);
+
+    if (found_device != nullptr &&
+        touchpad.type == ui::InputDeviceType::INPUT_DEVICE_INTERNAL) {
+      return touchpad.id;
+    }
+  }
+
+  return ui::InputDevice::kInvalidId;
+}
+
+bool IsFromInternalTrackpad(const ui::MouseEvent* event) {
+  return event->source_device_id() == GetInternalTrackpadDeviceId();
 }
 }  // namespace
 
@@ -45,13 +104,39 @@
     HandleKeyEvent(event.AsKeyEvent());
   }
 
-  if (event.IsMouseEvent() && IsFromInternalTrackpad(event)) {
-    return DiscardEvent(continuation);
+  if (event.IsMouseEvent()) {
+    return HandleMouseEvent(event.AsMouseEvent(), continuation);
   }
 
   return SendEvent(continuation, &event);
 }
 
+ui::EventDispatchDetails DisableTrackpadEventRewriter::HandleMouseEvent(
+    const ui::MouseEvent* event,
+    const Continuation continuation) {
+  DisableTrackpadMode disable_trackpad_mode =
+      Shell::Get()->accessibility_controller()->GetDisableTrackpadMode();
+  bool is_internal_trackpad_event = IsFromInternalTrackpad(event);
+  bool is_external_device_connected = IsExternalMouseOrTrackpadConnected();
+
+  switch (disable_trackpad_mode) {
+    case DisableTrackpadMode::kNever:
+      return SendEvent(continuation, event);
+
+    case DisableTrackpadMode::kAlways:
+      if (is_internal_trackpad_event) {
+        return DiscardEvent(continuation);
+      }
+      return SendEvent(continuation, event);
+
+    case DisableTrackpadMode::kOnExternalMouseConnected:
+      if (is_internal_trackpad_event && is_external_device_connected) {
+        return DiscardEvent(continuation);
+      }
+      return SendEvent(continuation, event);
+  }
+}
+
 void DisableTrackpadEventRewriter::HandleKeyEvent(const ui::KeyEvent* event) {
   // TODO(b/361611253): Make sure to check for control presses within a 10
   // second window.
@@ -68,5 +153,4 @@
     control_press_count_ = 0;
   }
 }
-
 }  // namespace ash
diff --git a/ash/accessibility/disable_trackpad_event_rewriter.h b/ash/accessibility/disable_trackpad_event_rewriter.h
index b444d517..9487099 100644
--- a/ash/accessibility/disable_trackpad_event_rewriter.h
+++ b/ash/accessibility/disable_trackpad_event_rewriter.h
@@ -8,6 +8,10 @@
 #include "ash/ash_export.h"
 #include "ui/events/event_rewriter.h"
 
+namespace ui {
+class MouseEvent;
+}
+
 namespace ash {
 
 // EventRewriter that cancels events from the built-in trackpad.
@@ -29,6 +33,8 @@
       const Continuation continuation) override;
 
   void HandleKeyEvent(const ui::KeyEvent* event);
+  ui::EventDispatchDetails HandleMouseEvent(const ui::MouseEvent* event,
+                                            const Continuation continuation);
 
   bool enabled_ = false;
   int control_press_count_ = 0;
diff --git a/ash/accessibility/disable_trackpad_event_rewriter_unittest.cc b/ash/accessibility/disable_trackpad_event_rewriter_unittest.cc
index 0b98ac3..f96b5b2 100644
--- a/ash/accessibility/disable_trackpad_event_rewriter_unittest.cc
+++ b/ash/accessibility/disable_trackpad_event_rewriter_unittest.cc
@@ -8,18 +8,73 @@
 #include <vector>
 
 #include "ash/accessibility/test_event_recorder.h"
+#include "ash/constants/ash_pref_names.h"
+#include "ash/public/cpp/accessibility_controller_enums.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/memory/raw_ptr.h"
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/events/base_event_utils.h"
+#include "ui/events/devices/device_data_manager_test_api.h"
+#include "ui/events/devices/input_device.h"
+#include "ui/events/devices/touchpad_device.h"
 #include "ui/events/event_rewriter.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/event_generator.h"
 
 namespace ash {
 
+namespace {
+
+const int kUsbMouseDeviceId = 20;
+const int kBluetoothMouseDeviceId = 25;
+const int kInternalTrackpadDeviceId = 30;
+const uint16_t kLogitechVID = 0x046d;
+const uint16_t kMousePID = 0xb034;
+
+ui::InputDevice GetSampleMouseUsb() {
+  return {kUsbMouseDeviceId, ui::INPUT_DEVICE_USB, "SampleMouseUsb"};
+}
+
+ui::InputDevice GetSampleMouseBluetooth() {
+  return {kBluetoothMouseDeviceId,
+          ui::INPUT_DEVICE_BLUETOOTH,
+          "SampleMouseBluetooth",
+          /* phys= */ "",
+          base::FilePath(),
+          kLogitechVID,
+          kMousePID,
+          /* version= */ 0};
+}
+
+ui::TouchpadDevice GetSampleTrackpadInternal() {
+  return {kInternalTrackpadDeviceId, ui::INPUT_DEVICE_INTERNAL, "touchpad"};
+}
+
+void SimulateOnlyInternalTrackpadConnected() {
+  ui::DeviceDataManagerTestApi().SetTouchpadDevices(
+      {GetSampleTrackpadInternal()});
+}
+
+void SimulateExternalMouseConnected() {
+  ui::DeviceDataManagerTestApi().SetMouseDevices(
+      {GetSampleMouseUsb(), GetSampleMouseBluetooth()});
+
+  SimulateOnlyInternalTrackpadConnected();
+}
+
+void SetDisableTrackpadMode(DisableTrackpadMode mode) {
+  PrefService* prefs =
+      Shell::Get()->session_controller()->GetLastActiveUserPrefService();
+  prefs->SetInteger(prefs::kAccessibilityDisableTrackpadMode,
+                    static_cast<int>(mode));
+}
+
+}  // namespace
+
 class DisableTrackpadEventRewriterTest : public AshTestBase {
  public:
   DisableTrackpadEventRewriterTest() = default;
@@ -101,8 +156,12 @@
             event_recorder()->events().back()->type());
 }
 
-TEST_F(DisableTrackpadEventRewriterTest, MouseButtonsCanceledWhenEnabled) {
+TEST_F(DisableTrackpadEventRewriterTest, MouseButtonsCanceledInAlwaysMode) {
   event_rewriter()->SetEnabled(true);
+  SetDisableTrackpadMode(DisableTrackpadMode::kAlways);
+  generator()->set_mouse_source_device_id(kInternalTrackpadDeviceId);
+  SimulateOnlyInternalTrackpadConnected();
+
   generator()->PressLeftButton();
   EXPECT_EQ(0U, event_recorder()->events().size());
   generator()->ReleaseLeftButton();
@@ -129,4 +188,42 @@
   }
 }
 
+TEST_F(DisableTrackpadEventRewriterTest,
+       InternalMouseCanceledWithExternalMouse) {
+  event_rewriter()->SetEnabled(true);
+  SetDisableTrackpadMode(DisableTrackpadMode::kOnExternalMouseConnected);
+  generator()->set_mouse_source_device_id(kInternalTrackpadDeviceId);
+
+  SimulateOnlyInternalTrackpadConnected();
+  generator()->PressLeftButton();
+  EXPECT_EQ(1U, event_recorder()->events().size());
+  generator()->ReleaseLeftButton();
+  EXPECT_EQ(2U, event_recorder()->events().size());
+
+  SimulateExternalMouseConnected();
+  generator()->PressLeftButton();
+  EXPECT_EQ(2U, event_recorder()->events().size());
+  generator()->ReleaseLeftButton();
+  EXPECT_EQ(2U, event_recorder()->events().size());
+}
+
+TEST_F(DisableTrackpadEventRewriterTest, ExternalMouseAllowedWhenConnected) {
+  event_rewriter()->SetEnabled(true);
+  SetDisableTrackpadMode(DisableTrackpadMode::kOnExternalMouseConnected);
+  generator()->set_mouse_source_device_id(kInternalTrackpadDeviceId);
+
+  SimulateOnlyInternalTrackpadConnected();
+  generator()->PressLeftButton();
+  EXPECT_EQ(1U, event_recorder()->events().size());
+  generator()->ReleaseLeftButton();
+  EXPECT_EQ(2U, event_recorder()->events().size());
+
+  SimulateExternalMouseConnected();
+  generator()->set_mouse_source_device_id(kUsbMouseDeviceId);
+  generator()->PressLeftButton();
+  EXPECT_EQ(3U, event_recorder()->events().size());
+  generator()->ReleaseLeftButton();
+  EXPECT_EQ(4U, event_recorder()->events().size());
+}
+
 }  // namespace ash
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index ea7759b..57ac5c96 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -838,6 +838,15 @@
       <message name="IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_PLAYLIST_UNSELECTED_ACCESSIBLE_DESCRIPTION" desc="The accessible description for a unselected playlist image button on the focus panel">
         Unselected
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_YOUTUBE_MUSIC" desc="The text for the error toast when the focus mode isn't connected to the YouTube Music">
+        Couldn’t connect to YouTube Music
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_FOCUS_SOUNDS" desc="The text for the error toast when the focus mode isn't connected to the Focus sounds">
+        Couldn’t connect to Focus sounds
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_FAILED_PLAYING_SOUNDS" desc="The text for the error toast when the player couldn't play the Soundscape playlist">
+        Couldn’t play Focus sounds
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_FOCUS_MODE_DO_NOT_DISTURB" desc="The label text shown on the left side of do not disturb button in Focus mode settings panel.">
         Do Not Disturb while in Focus
       </message>
@@ -5239,6 +5248,12 @@
       <message name="IDS_ASH_AUTH_TEXTFIELD_PASSWORD_ACCESSIBLE_NAME" desc="The accessibility text for the authentication password textfield.">
         Password
       </message>
+      <message name="IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_SHOW" desc="Text to be spoken when the focus is set to the display pin button (in Show state) on login screen and in-session auth dialog.">
+        Show PIN
+      </message>
+      <message name="IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_HIDE" desc="Text to be spoken when the focus is set to the display pin button (in Hide state) on login screen and in-session auth dialog.">
+        Hide PIN
+      </message>
 
       <!-- Fast Pair strings -->
       <message name="IDS_FAST_PAIR_CONNECTION_ERROR_TITLE" desc="Notification title shown when a there is a Fast Pair connection error.">
diff --git a/ash/ash_strings_grd/IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_HIDE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_HIDE.png.sha1
new file mode 100644
index 0000000..3b95dfd
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_HIDE.png.sha1
@@ -0,0 +1 @@
+1919b47a3390db300f267f281de02ba6f584eddf
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_SHOW.png.sha1 b/ash/ash_strings_grd/IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_SHOW.png.sha1
new file mode 100644
index 0000000..883b4a2b
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_SHOW.png.sha1
@@ -0,0 +1 @@
+4d2885183c435ac97a4b1cea2ccc3c339bd65ea0
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_FOCUS_SOUNDS.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_FOCUS_SOUNDS.png.sha1
new file mode 100644
index 0000000..7191f18
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_FOCUS_SOUNDS.png.sha1
@@ -0,0 +1 @@
+17d3a9a78a1849c03718db434aabb7b878f189fc
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_YOUTUBE_MUSIC.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_YOUTUBE_MUSIC.png.sha1
new file mode 100644
index 0000000..e3bb9584
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_YOUTUBE_MUSIC.png.sha1
@@ -0,0 +1 @@
+1ffe509181102a1f6448b7cac3d25ad79826070a
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_FAILED_PLAYING_SOUNDS.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_FAILED_PLAYING_SOUNDS.png.sha1
new file mode 100644
index 0000000..86ca088
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_FAILED_PLAYING_SOUNDS.png.sha1
@@ -0,0 +1 @@
+cec7ab8afd981fcbe8d443649047775009f74f67
\ No newline at end of file
diff --git a/ash/auth/views/auth_input_row_view.cc b/ash/auth/views/auth_input_row_view.cc
index 5ed426e..a4da9add 100644
--- a/ash/auth/views/auth_input_row_view.cc
+++ b/ash/auth/views/auth_input_row_view.cc
@@ -264,6 +264,8 @@
 
   submit_button_->SetTooltipText(
       l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SUBMIT_BUTTON_ACCESSIBLE_NAME));
+
+  submit_button_->GetViewAccessibility().SetRole(ax::mojom::Role::kButton);
 }
 
 void AuthInputRowView::CreateAndConfigureDisplayTextButton() {
@@ -272,10 +274,21 @@
           base::BindRepeating(&AuthInputRowView::ToggleTextDisplayingState,
                               base::Unretained(this))));
 
-  display_text_button_->SetTooltipText(l10n_util::GetStringUTF16(
-      IDS_ASH_LOGIN_DISPLAY_PASSWORD_BUTTON_ACCESSIBLE_NAME_SHOW));
-  display_text_button_->SetToggledTooltipText(l10n_util::GetStringUTF16(
-      IDS_ASH_LOGIN_DISPLAY_PASSWORD_BUTTON_ACCESSIBLE_NAME_HIDE));
+  switch (auth_type_) {
+    case AuthType::kPassword:
+      display_text_button_->SetTooltipText(l10n_util::GetStringUTF16(
+          IDS_ASH_LOGIN_DISPLAY_PASSWORD_BUTTON_ACCESSIBLE_NAME_SHOW));
+      display_text_button_->SetToggledTooltipText(l10n_util::GetStringUTF16(
+          IDS_ASH_LOGIN_DISPLAY_PASSWORD_BUTTON_ACCESSIBLE_NAME_HIDE));
+      break;
+    case AuthType::kPin:
+      display_text_button_->SetTooltipText(l10n_util::GetStringUTF16(
+          IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_SHOW));
+      display_text_button_->SetToggledTooltipText(l10n_util::GetStringUTF16(
+          IDS_ASH_AUTH_DISPLAY_PIN_BUTTON_ACCESSIBLE_NAME_HIDE));
+      break;
+  }
+
   display_text_button_->SetFocusBehavior(FocusBehavior::ALWAYS);
   display_text_button_->SetInstallFocusRingOnFocus(true);
   views::FocusRing::Get(display_text_button_)
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index 64afa2e..ec21f944 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -2230,12 +2230,7 @@
   }
 
   RecordCaptureModeEntryType(entry_type);
-  // Reset the user capture region if enough time has passed as it can be
-  // annoying to still have the old capture region from the previous session
-  // long time ago.
-  if (!user_capture_region_.IsEmpty() &&
-      base::TimeTicks::Now() - last_capture_region_update_time_ >
-          kResetCaptureRegionDuration) {
+  if (ShouldClearCaptureRegion(behavior_type)) {
     SetUserCaptureRegion(gfx::Rect(), /*by_user=*/false);
   }
 
@@ -2379,6 +2374,17 @@
   CaptureImage(capture_params, BuildImagePath(), GetBehavior(behavior_type));
 }
 
+bool CaptureModeController::ShouldClearCaptureRegion(
+    BehaviorType behavior_type) const {
+  // Reset the user capture region if enough time has passed as it can be
+  // annoying to still have the old capture region from the previous session
+  // long time ago, or if the active behavior is sunfish behavior.
+  return !user_capture_region_.IsEmpty() &&
+         (base::TimeTicks::Now() - last_capture_region_update_time_ >
+              kResetCaptureRegionDuration ||
+          behavior_type == BehaviorType::kSunfish);
+}
+
 CaptureModeSaveToLocation CaptureModeController::GetSaveToOption(
     const base::FilePath& path) {
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index f746125..4b489db4 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -638,6 +638,8 @@
   void PerformScreenshotOfGivenWindow(aura::Window* given_window,
                                       BehaviorType behavior_type);
 
+  bool ShouldClearCaptureRegion(BehaviorType behavior_type) const;
+
   // Gets the corresponding `SaveLocation` enum value on the given `path`.
   CaptureModeSaveToLocation GetSaveToOption(const base::FilePath& path);
 
diff --git a/ash/capture_mode/sunfish_unittest.cc b/ash/capture_mode/sunfish_unittest.cc
index fbe4ae1..6e3700e 100644
--- a/ash/capture_mode/sunfish_unittest.cc
+++ b/ash/capture_mode/sunfish_unittest.cc
@@ -181,6 +181,32 @@
   EXPECT_EQ(file_saved_path.DirName(), default_folder);
 }
 
+// Tests that the capture region is reset if sunfish is restarted.
+TEST_F(SunfishTest, ResetCaptureRegion) {
+  // Start sunfish, then select a region.
+  auto* controller = CaptureModeController::Get();
+  controller->StartSunfishSession();
+  ASSERT_EQ(
+      BehaviorType::kSunfish,
+      controller->capture_mode_session()->active_behavior()->behavior_type());
+
+  const gfx::Rect capture_region(100, 100, 600, 500);
+  SelectCaptureModeRegion(GetEventGenerator(), capture_region,
+                          /*release_mouse=*/false);
+  EXPECT_EQ(capture_region, controller->user_capture_region());
+
+  // Exit sunfish, then restart sunfish.
+  PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
+  ASSERT_FALSE(controller->IsActive());
+
+  controller->StartSunfishSession();
+  EXPECT_TRUE(controller->user_capture_region().IsEmpty());
+  CaptureModeSessionTestApi test_api(controller->capture_mode_session());
+  auto* capture_label = test_api.GetCaptureLabelInternalView();
+  EXPECT_TRUE(capture_label->GetVisible());
+  EXPECT_EQ(u"Drag to select an area to search", capture_label->GetText());
+}
+
 // Tests the sunfish capture mode bar view.
 TEST_F(SunfishTest, CaptureBarView) {
   auto* controller = CaptureModeController::Get();
diff --git a/ash/components/arc/metrics/arc_wm_metrics.cc b/ash/components/arc/metrics/arc_wm_metrics.cc
index eb8f2b28..8a756e8c 100644
--- a/ash/components/arc/metrics/arc_wm_metrics.cc
+++ b/ash/components/arc/metrics/arc_wm_metrics.cc
@@ -359,9 +359,9 @@
   const bool from_normal_to_maximized =
       IsNormalWindowStateType(
           chromeos::ToWindowStateType(old_window_show_state)) &&
-      new_window_show_state == ui::WindowShowState::SHOW_STATE_MAXIMIZED;
+      new_window_show_state == ui::SHOW_STATE_MAXIMIZED;
   const bool from_any_to_minimized =
-      new_window_show_state == ui::WindowShowState::SHOW_STATE_MINIMIZED;
+      new_window_show_state == ui::SHOW_STATE_MINIMIZED;
   if (from_normal_to_maximized || from_any_to_minimized) {
     state_change_observing_windows_.emplace(
         window, std::make_unique<WindowStateChangeObserver>(
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index e3bb2a1..c7501651 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -106,7 +106,7 @@
 // Adds support for allowing or disabling APN modification by policy.
 BASE_FEATURE(kAllowApnModificationPolicy,
              "AllowApnModificationPolicy",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Controls whether the annotator feature is enabled in ChromeOS.
 BASE_FEATURE(kAnnotatorMode,
@@ -780,7 +780,7 @@
 // Enables settings to control internal display brightness and auto-brightness.
 BASE_FEATURE(kEnableBrightnessControlInSettings,
              "EnableBrightnessControlInSettings",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables an update to 24px rounded corners for all `TrayBubbleView`s and
 // `AppListBubbleView`.
@@ -807,7 +807,7 @@
 // Enables or disables keyboard backlight control in settings.
 BASE_FEATURE(kEnableKeyboardBacklightControlInSettings,
              "EnableKeyboardBacklightControlInSettings",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables or disables keyboard backlight toggle.
 BASE_FEATURE(kEnableKeyboardBacklightToggle,
@@ -1333,7 +1333,7 @@
 // Enables the Game Dashboard for additional PWA games.
 BASE_FEATURE(kGameDashboardGamePWAs,
              "GameDashboardGamePWAs",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables additional games being evaluated for the Game Dashboard.
 BASE_FEATURE(kGameDashboardGamesInTest,
@@ -3311,7 +3311,7 @@
 // Enables a new Welcome Experience for first-time peripheral connections.
 BASE_FEATURE(kWelcomeExperience,
              "WelcomeExperience",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // kWelcomeExperienceTestUnsupportedDevices enables the new device Welcome
 // Experience to be tested on external devices that are not officially
diff --git a/ash/login/README.md b/ash/login/README.md
index fce4aac..4130201 100644
--- a/ash/login/README.md
+++ b/ash/login/README.md
@@ -9,9 +9,9 @@
 method calls sent from ash to chrome & handles messages from chrome to ash.
 Forwards some of the calls to the `Delegate`.
 
-- `//chrome/browser/ash/login/ui/`:
+- `//chrome/browser/ui/ash/login/`:
   - This folder contains implementations of login and OOBE UIs.
-  - [`LoginDisplayHostMojo`](/chrome/browser/ash/login/ui/
+  - [`LoginDisplayHostMojo`](/chrome/browser/ui/ash/login/
 login_display_host_mojo.h) - a `LoginDisplayHost` instance that implements
 `LoginScreenClient` and sends requests to the views-based sign in. Handles calls
 like `HandleAuthenticateUserWith...()`. Owned by
diff --git a/ash/login/ui/login_password_view.cc b/ash/login/ui/login_password_view.cc
index b8627ba..d4c264c 100644
--- a/ash/login/ui/login_password_view.cc
+++ b/ash/login/ui/login_password_view.cc
@@ -512,9 +512,8 @@
 
 gfx::Size LoginPasswordView::CalculatePreferredSize(
     const views::SizeBounds& available_size) const {
-  views::SizeBounds content_available_size(available_size);
-  content_available_size.set_width(kPasswordTotalWidthDp);
-  gfx::Size size = views::View::CalculatePreferredSize(content_available_size);
+  gfx::Size size = views::View::CalculatePreferredSize(
+      views::SizeBounds(kPasswordTotalWidthDp, {}));
   size.set_width(kPasswordTotalWidthDp);
   return size;
 }
diff --git a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc
index 454c9ce..27bace6a 100644
--- a/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc
+++ b/ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc
@@ -24,11 +24,11 @@
 namespace {
 
 const char kUserDevicesUrl[] =
-    "https://nearbydevices-pa.googleapis.com/v1/user/devices"
+    "https://nearbydevices-pa.googleapis.com/v1/userdevices"
     "?key=%s&alt=proto";
 
 const char kUserDeleteDeviceUrl[] =
-    "https://nearbydevices-pa.googleapis.com/v1/user/device/%s"
+    "https://nearbydevices-pa.googleapis.com/v1/userdevices/%s"
     "?key=%s&alt=proto";
 
 const net::PartialNetworkTrafficAnnotationTag kTrafficAnnotation =
@@ -61,7 +61,7 @@
 
 std::unique_ptr<HttpFetcher> CreateHttpFetcher() {
   return std::make_unique<OAuthHttpFetcher>(
-      kTrafficAnnotation, GaiaConstants::kCloudPlatformProjectsOAuth2Scope);
+      kTrafficAnnotation, GaiaConstants::kNearbyDevicesOAuth2Scope);
 }
 
 GURL GetUserDevicesUrl() {
diff --git a/ash/system/brightness/brightness_controller_chromeos.cc b/ash/system/brightness/brightness_controller_chromeos.cc
index d5a3924e..1357132 100644
--- a/ash/system/brightness/brightness_controller_chromeos.cc
+++ b/ash/system/brightness/brightness_controller_chromeos.cc
@@ -287,13 +287,18 @@
 
 void BrightnessControllerChromeos::OnFocusPod(const AccountId& account_id) {
   active_account_id_ = account_id;
-  if (!features::IsBrightnessControlInSettingsEnabled()) {
+  if (!features::IsBrightnessControlInSettingsEnabled() ||
+      IsInitialBrightnessSetByPolicy()) {
     return;
   }
-  if (IsInitialBrightnessSetByPolicy()) {
-    return;
+
+  session_manager::SessionState session_state =
+      Shell::Get()->session_controller()->GetSessionState();
+  if (session_state == session_manager::SessionState::LOGIN_PRIMARY ||
+      session_state == session_manager::SessionState::LOGIN_SECONDARY) {
+    // Restore brightness settings only when device reboots.
+    RestoreBrightnessSettings(account_id);
   }
-  RestoreBrightnessSettings(account_id);
 }
 
 void BrightnessControllerChromeos::RestoreBrightnessSettings(
diff --git a/ash/system/brightness/brightness_controller_chromeos_unittest.cc b/ash/system/brightness/brightness_controller_chromeos_unittest.cc
index 0a91ccb..91c8379 100644
--- a/ash/system/brightness/brightness_controller_chromeos_unittest.cc
+++ b/ash/system/brightness/brightness_controller_chromeos_unittest.cc
@@ -1241,6 +1241,8 @@
       GetAmbientLightSensorDisabledReasonPrefValue(known_user, account_id));
 
   // Simulate reboot, and log in again.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   known_user.SetPath(account_id, prefs::kInternalDisplayScreenBrightnessPercent,
                      std::make_optional<base::Value>(30.0));
   login_data_dispatcher()->NotifyFocusPod(account_id);
@@ -1251,6 +1253,8 @@
   ExpectBrightnessPercent(30.0, "Brightness percent should be restored.");
 
   // Simulate reboot, and log in the third time.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   login_data_dispatcher()->NotifyFocusPod(account_id);
 
   // ALS and brightness should remain the same as last reboot.
@@ -1301,6 +1305,8 @@
       GetAmbientLightSensorDisabledReasonPrefValue(known_user, account_id));
 
   // Simulate reboot, and log in again.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   known_user.SetPath(account_id, prefs::kInternalDisplayScreenBrightnessPercent,
                      std::make_optional<base::Value>(30.0));
   login_data_dispatcher()->NotifyFocusPod(account_id);
@@ -1309,6 +1315,8 @@
   ExpectAmbientLightSensorEnabled(true, "ALS is re-enabled.");
 
   // Simulate reboot, and log in the third time.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   login_data_dispatcher()->NotifyFocusPod(account_id);
 
   // ALS should remain enabled.
@@ -1317,6 +1325,48 @@
 }
 
 TEST_F(BrightnessControllerChromeosTest,
+       BrightnessSettingsUnchanged_DeviceLocked) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kEnableBrightnessControlInSettings);
+
+  // Set initial ALS status and brightness level.
+  power_manager::SetAmbientLightSensorEnabledRequest request;
+  request.set_sensor_enabled(true);
+  power_manager_client()->SetAmbientLightSensorEnabled(request);
+  power_manager_client()->set_screen_brightness_percent(kInitialBrightness);
+
+  // Log in
+  ClearLogin();
+  AccountId account_id = AccountId::FromUserEmail(kUserEmail);
+  user_manager::KnownUser known_user(local_state());
+  SimulateUserLogin(kUserEmail);
+
+  // Disable ALS using the brightness key.
+  SetAmbientLightSensorEnabled(
+      false,
+      power_manager::AmbientLightSensorChange_Cause_BRIGHTNESS_USER_REQUEST);
+
+  // Current status: Als is turned off, and current brightness is
+  // kInitialBrightness.
+  ExpectAmbientLightSensorEnabled(
+      false,
+      "Ambient light sensor is disabled, the request is from brightness key.");
+  ExpectBrightnessPercent(kInitialBrightness,
+                          "Current brightness should be kInitialBrightness.");
+
+  // Simulate device lock and re-login.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::ACTIVE);
+  login_data_dispatcher()->NotifyFocusPod(account_id);
+
+  // Als should not be re-enabled, although it was not previously disabled from
+  // settings app. The brightness percent should still be kInitialBrightness.
+  ExpectAmbientLightSensorEnabled(false, "ALS remain disabled.");
+  ExpectBrightnessPercent(kInitialBrightness,
+                          "Brightness should remain unchanged.");
+}
+
+TEST_F(BrightnessControllerChromeosTest,
        RestoreAutoBrightnessForNewUser_FlagEnabled) {
   scoped_feature_list_.InitAndEnableFeature(
       features::kEnableBrightnessControlInSettings);
@@ -1548,6 +1598,8 @@
   EXPECT_EQ(GetBrightnessPrefValue(known_user, account_id), 10.0);
 
   // Simulate reboot.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   LoginScreenFocusAccount(account_id);
 
   // Expect the brightness is restored to 10%.
@@ -1597,7 +1649,9 @@
   SetBatteryPower();
 
   // Simulate reboot, and log in.
-  LoginScreenFocusAccount(account_id);
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
+  SimulateUserLogin(kUserEmail);
 
   // Expect the brightness is not restored to 10%.
   brightness_control_delegate()->GetBrightnessPercent(
diff --git a/ash/system/camera/camera_effects_controller.cc b/ash/system/camera/camera_effects_controller.cc
index b4c79fd..fb83b54 100644
--- a/ash/system/camera/camera_effects_controller.cc
+++ b/ash/system/camera/camera_effects_controller.cc
@@ -453,6 +453,8 @@
       media::CameraHalDispatcherImpl::GetInstance());
 
   Shell::Get()->autozoom_controller()->AddObserver(this);
+
+  VideoConferenceTrayController::Get()->GetEffectsManager().AddObserver(this);
 }
 
 CameraEffectsController::~CameraEffectsController() {
@@ -586,16 +588,38 @@
       std::move(callback));
 }
 
+bool CameraEffectsController::IsEligibleForBackgroundReplace() {
+  SessionControllerImpl* session_controller =
+      Shell::Get()->session_controller();
+  if (!session_controller) {
+    return false;
+  }
+
+  AccountId account_id =
+      Shell::Get()->session_controller()->GetActiveAccountId();
+  return features::IsVcBackgroundReplaceEnabled() &&
+         std::get<0>(Shell::Get()->session_controller()->IsEligibleForSeaPen(
+             account_id));
+}
+
+bool CameraEffectsController::IsVcBackgroundAllowedByEnterprise() {
+  SessionControllerImpl* session_controller =
+      Shell::Get()->session_controller();
+  if (!session_controller) {
+    return false;
+  }
+
+  AccountId account_id = session_controller->GetActiveAccountId();
+  return std::get<1>(session_controller->IsEligibleForSeaPen(account_id));
+}
+
 // Set the `camera_background_img_dir_` when the `account_id` becomes active.
 void CameraEffectsController::OnActiveUserSessionChanged(
     const AccountId& account_id) {
-  is_eligible_for_background_replace_ =
-      features::IsVcBackgroundReplaceEnabled() &&
-      std::get<0>(
-          Shell::Get()->session_controller()->IsEligibleForSeaPen(account_id));
+  is_eligible_for_background_replace_ = IsEligibleForBackgroundReplace();
 
-  is_background_replace_disabled_by_enterprise_ = !std::get<1>(
-      Shell::Get()->session_controller()->IsEligibleForSeaPen(account_id));
+  is_background_replace_disabled_by_enterprise_ =
+      !IsVcBackgroundAllowedByEnterprise();
 
   const base::FilePath profile_path =
       Shell::Get()->session_controller()->GetProfilePath(account_id);
@@ -805,6 +829,35 @@
   }
 }
 
+void CameraEffectsController::OnVideoConferenceBubbleOpened() {
+  const bool is_eligible = IsEligibleForBackgroundReplace();
+  const bool is_enterprise_disabled = !IsVcBackgroundAllowedByEnterprise();
+
+  // If the updated eligible state is false, no further action required.
+  if (!is_eligible) {
+    return;
+  }
+
+  // If the background replace is already eligibled but no changes in enterprise
+  // enabled state, no further action required.
+  if (is_eligible_for_background_replace_ &&
+      is_enterprise_disabled == is_background_replace_disabled_by_enterprise_) {
+    return;
+  }
+
+  // If background blur effect not yet added, do nothing.
+  if (!GetEffectById(VcEffectId::kBackgroundBlur)) {
+    return;
+  }
+
+  // Update Background Blur effect if background replace eligible state changes
+  // from false -> true or enterprise enabled state changes.
+  is_eligible_for_background_replace_ = true;
+  is_background_replace_disabled_by_enterprise_ = is_enterprise_disabled;
+  RemoveEffect(VcEffectId::kBackgroundBlur);
+  AddBackgroundBlurEffect();
+}
+
 void CameraEffectsController::OnAutozoomControlEnabledChanged(bool enabled) {
   if (!enabled) {
     RemoveEffect(VcEffectId::kCameraFraming);
@@ -1044,52 +1097,7 @@
     return;
   }
 
-  // If background blur UI controls are present, construct the effect and its
-  // states.
-  if (IsEffectControlAvailable(cros::mojom::CameraEffect::kBackgroundBlur)) {
-    auto effect = std::make_unique<VcHostedEffect>(
-        /*type=*/VcEffectType::kSetValue,
-        /*get_state_callback=*/
-        base::BindRepeating(&CameraEffectsController::GetEffectState,
-                            base::Unretained(this),
-                            VcEffectId::kBackgroundBlur),
-        /*effect_id=*/VcEffectId::kBackgroundBlur);
-    effect->set_label_text(l10n_util::GetStringUTF16(
-        IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_NAME));
-    effect->set_effects_delegate(this);
-    AddBackgroundBlurStateToEffect(
-        effect.get(), kVideoConferenceBackgroundBlurOffIcon,
-        /*state_value=*/BackgroundBlurPrefValue::kOff,
-        /*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_OFF,
-        video_conference::BubbleViewID::kBackgroundBlurOffButton,
-        /*is_disabled_by_enterprise=*/false);
-    AddBackgroundBlurStateToEffect(
-        effect.get(), kVideoConferenceBackgroundBlurLightIcon,
-        /*state_value=*/BackgroundBlurPrefValue::kLight,
-        /*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_LIGHT,
-        video_conference::BubbleViewID::kBackgroundBlurLightButton,
-        /*is_disabled_by_enterprise=*/false);
-    AddBackgroundBlurStateToEffect(
-        effect.get(), kVideoConferenceBackgroundBlurMaximumIcon,
-        /*state_value=*/BackgroundBlurPrefValue::kMaximum,
-        /*string_id=*/
-        IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_FULL,
-        video_conference::BubbleViewID::kBackgroundBlurFullButton,
-        /*is_disabled_by_enterprise=*/false);
-
-    if (is_eligible_for_background_replace_) {
-      AddBackgroundBlurStateToEffect(
-          effect.get(), kAiImageIcon,
-          /*state_value=*/BackgroundBlurPrefValue::kImage,
-          /*string_id=*/
-          IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_IMAGE,
-          video_conference::BubbleViewID::kBackgroundBlurImageButton,
-          /*is_disabled_by_enterprise=*/
-          is_background_replace_disabled_by_enterprise_);
-    }
-    effect->set_dependency_flags(VcHostedEffect::ResourceDependency::kCamera);
-    AddEffect(std::move(effect));
-  }
+  AddBackgroundBlurEffect();
 
   // If portrait relight UI controls are present, construct the effect and its
   // state. If the Studio Look feature is available, the same UI control is used
@@ -1146,6 +1154,55 @@
   }
 }
 
+void CameraEffectsController::AddBackgroundBlurEffect() {
+  if (!IsEffectControlAvailable(cros::mojom::CameraEffect::kBackgroundBlur)) {
+    return;
+  }
+  // If background blur UI controls are present, construct the effect and its
+  // states.
+  auto effect = std::make_unique<VcHostedEffect>(
+      /*type=*/VcEffectType::kSetValue,
+      /*get_state_callback=*/
+      base::BindRepeating(&CameraEffectsController::GetEffectState,
+                          base::Unretained(this), VcEffectId::kBackgroundBlur),
+      /*effect_id=*/VcEffectId::kBackgroundBlur);
+  effect->set_label_text(l10n_util::GetStringUTF16(
+      IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_NAME));
+  effect->set_effects_delegate(this);
+  AddBackgroundBlurStateToEffect(
+      effect.get(), kVideoConferenceBackgroundBlurOffIcon,
+      /*state_value=*/BackgroundBlurPrefValue::kOff,
+      /*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_OFF,
+      video_conference::BubbleViewID::kBackgroundBlurOffButton,
+      /*is_disabled_by_enterprise=*/false);
+  AddBackgroundBlurStateToEffect(
+      effect.get(), kVideoConferenceBackgroundBlurLightIcon,
+      /*state_value=*/BackgroundBlurPrefValue::kLight,
+      /*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_LIGHT,
+      video_conference::BubbleViewID::kBackgroundBlurLightButton,
+      /*is_disabled_by_enterprise=*/false);
+  AddBackgroundBlurStateToEffect(
+      effect.get(), kVideoConferenceBackgroundBlurMaximumIcon,
+      /*state_value=*/BackgroundBlurPrefValue::kMaximum,
+      /*string_id=*/
+      IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_FULL,
+      video_conference::BubbleViewID::kBackgroundBlurFullButton,
+      /*is_disabled_by_enterprise=*/false);
+
+  if (is_eligible_for_background_replace_) {
+    AddBackgroundBlurStateToEffect(
+        effect.get(), kAiImageIcon,
+        /*state_value=*/BackgroundBlurPrefValue::kImage,
+        /*string_id=*/
+        IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_IMAGE,
+        video_conference::BubbleViewID::kBackgroundBlurImageButton,
+        /*is_disabled_by_enterprise=*/
+        is_background_replace_disabled_by_enterprise_);
+  }
+  effect->set_dependency_flags(VcHostedEffect::ResourceDependency::kCamera);
+  AddEffect(std::move(effect));
+}
+
 void CameraEffectsController::AddBackgroundBlurStateToEffect(
     VcHostedEffect* effect,
     const gfx::VectorIcon& icon,
diff --git a/ash/system/camera/camera_effects_controller.h b/ash/system/camera/camera_effects_controller.h
index eb29a32..58fba87 100644
--- a/ash/system/camera/camera_effects_controller.h
+++ b/ash/system/camera/camera_effects_controller.h
@@ -14,6 +14,7 @@
 #include "ash/public/cpp/wallpaper/sea_pen_image.h"
 #include "ash/system/camera/autozoom_observer.h"
 #include "ash/system/video_conference/effects/video_conference_tray_effects_delegate.h"
+#include "ash/system/video_conference/video_conference_tray_controller.h"
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
@@ -34,10 +35,12 @@
 
 // CameraEffectsController is the interface for any object in ash to
 // enable/change camera effects.
-class ASH_EXPORT CameraEffectsController : public AutozoomObserver,
-                                           public media::CameraEffectObserver,
-                                           public SessionObserver,
-                                           public VcEffectsDelegate {
+class ASH_EXPORT CameraEffectsController
+    : public AutozoomObserver,
+      public media::CameraEffectObserver,
+      public SessionObserver,
+      public VcEffectsDelegate,
+      public VideoConferenceTrayEffectsManager::Observer {
  public:
   // Enum that represents the value persisted  to `prefs::kBackgroundBlur`,
   // which is the "ultimate source of truth" for the background blur setting.
@@ -143,6 +146,10 @@
       base::OnceCallback<void(const std::optional<BackgroundImageInfo>&)>
           callback);
 
+  bool IsEligibleForBackgroundReplace();
+
+  bool IsVcBackgroundAllowedByEnterprise();
+
   // SessionObserver:
   void OnActiveUserSessionChanged(const AccountId& account_id) override;
   void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
@@ -160,6 +167,16 @@
   void OnCameraEffectChanged(
       const cros::mojom::EffectsConfigPtr& new_effects) final;
 
+  // VideoConferenceTrayEffectsManager::Observer
+  // When video conference bubble is opened, update background blur effect in
+  // two cases:
+  // - Add Image state when the background replace eligible state changes from
+  // false -> true. This happens at most one time for enterprise users.
+  // - Disable/enable Image state button when background replace is already
+  // eligibled and enterprise policy setting changes. VC Background policy is
+  // dynamic-refreshed and UI should update if any changes.
+  void OnVideoConferenceBubbleOpened() override;
+
   void bypass_set_camera_effects_for_testing(bool in_testing_mode) {
     in_testing_mode_ = in_testing_mode;
   }
@@ -227,6 +244,8 @@
   // exposed via the UI.
   void InitializeEffectControls();
 
+  void AddBackgroundBlurEffect();
+
   // Adds a `std::unique_ptr<VcEffectState>` to `effect`, where `effect` is
   // assumed to be that of camera background blur.
   void AddBackgroundBlurStateToEffect(VcHostedEffect* effect,
diff --git a/ash/system/camera/camera_effects_controller_unittest.cc b/ash/system/camera/camera_effects_controller_unittest.cc
index c5ecbf2b..0f4347f0 100644
--- a/ash/system/camera/camera_effects_controller_unittest.cc
+++ b/ash/system/camera/camera_effects_controller_unittest.cc
@@ -884,4 +884,73 @@
   EXPECT_EQ(effects[0]->GetNumStates(), 3);
 }
 
+TEST_F(CameraEffectsControllerTest, UpdateBackgroundBlurImageState) {
+  // Set is_eligible_for_background_replace to false so that the image button
+  // will not be constructed.
+  GetSessionControllerClient()->set_is_eligible_for_background_replace(
+      {false, false});
+  SimulateUserLogin(kTestAccount);
+
+  // Update media status to make the video conference tray visible.
+  VideoConferenceMediaState state;
+  state.has_media_app = true;
+  state.has_camera_permission = true;
+  state.has_microphone_permission = true;
+  state.is_capturing_screen = true;
+  tray_controller()->UpdateWithMediaState(state);
+
+  auto effects = VideoConferenceTrayController::Get()
+                     ->GetEffectsManager()
+                     .GetSetValueEffects();
+
+  EXPECT_EQ(effects.size(), 1u);
+  EXPECT_EQ(effects[0]->label_text(), u"Background");
+  // Verify that only three states are constructed; the forth one is the image
+  // button.
+  EXPECT_EQ(effects[0]->GetNumStates(), 3);
+
+  // Set background replace eligible state to true and enterprise enabled state
+  // to false so that the image button is added but disabled.
+  GetSessionControllerClient()->set_is_eligible_for_background_replace(
+      {true, false});
+  auto* vc_tray = StatusAreaWidgetTestHelper::GetStatusAreaWidget()
+                      ->video_conference_tray();
+
+  // Open the vc bubble to notify bubble opened and update Background Blur
+  // effect.
+  LeftClickOn(vc_tray->toggle_bubble_button());
+
+  effects = VideoConferenceTrayController::Get()
+                ->GetEffectsManager()
+                .GetSetValueEffects();
+
+  // Now four states are constructed and the forth one is the image button.
+  EXPECT_EQ(effects[0]->GetNumStates(), 4) << " four states are constructed";
+  const VcEffectState* imageState = effects[0]->GetState(/*index=*/3);
+  EXPECT_EQ(imageState->view_id(),
+            video_conference::BubbleViewID::kBackgroundBlurImageButton);
+  EXPECT_TRUE(imageState->is_disabled_by_enterprise());
+
+  // Update VC Background enterprise enabled state to true so that the Image
+  // button is enabled.
+  GetSessionControllerClient()->set_is_eligible_for_background_replace(
+      {true, true});
+  // Close the video conference bubble.
+  LeftClickOn(vc_tray->toggle_bubble_button());
+  // Reopen the bubble to trigger updating Background Blur effect again.
+  LeftClickOn(vc_tray->toggle_bubble_button());
+
+  effects = VideoConferenceTrayController::Get()
+                ->GetEffectsManager()
+                .GetSetValueEffects();
+
+  // The image button is now enabled.
+  EXPECT_EQ(effects[0]->GetNumStates(), 4)
+      << "still four states for Background Blur effect";
+  const VcEffectState* newImageState = effects[0]->GetState(/*index=*/3);
+  EXPECT_EQ(newImageState->view_id(),
+            video_conference::BubbleViewID::kBackgroundBlurImageButton);
+  EXPECT_FALSE(newImageState->is_disabled_by_enterprise());
+}
+
 }  // namespace ash
diff --git a/ash/system/focus_mode/sounds/focus_mode_sounds_controller.cc b/ash/system/focus_mode/sounds/focus_mode_sounds_controller.cc
index d9cae8b5..ef79706 100644
--- a/ash/system/focus_mode/sounds/focus_mode_sounds_controller.cc
+++ b/ash/system/focus_mode/sounds/focus_mode_sounds_controller.cc
@@ -354,6 +354,12 @@
                          base::BindOnce(&OnTrackFetched, std::move(callback)));
 }
 
+void FocusModeSoundsController::ReportPlayerError() {
+  for (auto& observer : observers_) {
+    observer.OnPlayerError();
+  }
+}
+
 void FocusModeSoundsController::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/ash/system/focus_mode/sounds/focus_mode_sounds_controller.h b/ash/system/focus_mode/sounds/focus_mode_sounds_controller.h
index 2d109f2..18d335b 100644
--- a/ash/system/focus_mode/sounds/focus_mode_sounds_controller.h
+++ b/ash/system/focus_mode/sounds/focus_mode_sounds_controller.h
@@ -64,6 +64,8 @@
     virtual void OnSelectedPlaylistChanged() = 0;
     // Called when the state of `selected_playlist_` has been changed.
     virtual void OnPlaylistStateChanged() {}
+    // Called when the media player encounters an error.
+    virtual void OnPlayerError() {}
   };
 
   FocusModeSoundsController();
@@ -83,6 +85,9 @@
       const std::optional<FocusModeSoundsDelegate::Track>&)>;
   void GetNextTrack(GetNextTrackCallback callback);
 
+  // Called by `FocusModeTrackProvider::ReportPlayerError`.
+  void ReportPlayerError();
+
   const std::vector<std::unique_ptr<Playlist>>& soundscape_playlists() const {
     return soundscape_playlists_;
   }
diff --git a/ash/system/focus_mode/sounds/focus_mode_sounds_view.cc b/ash/system/focus_mode/sounds/focus_mode_sounds_view.cc
index 7d33636..96856271 100644
--- a/ash/system/focus_mode/sounds/focus_mode_sounds_view.cc
+++ b/ash/system/focus_mode/sounds/focus_mode_sounds_view.cc
@@ -10,6 +10,7 @@
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/style/error_message_toast.h"
 #include "ash/style/pill_button.h"
 #include "ash/style/rounded_container.h"
 #include "ash/style/tab_slider.h"
@@ -55,6 +56,12 @@
 constexpr float kOfflineStateOpacity = 0.38f;
 constexpr auto kLabelPadding = gfx::Insets::VH(0, 40);
 
+constexpr auto kErrorMessagePadding = gfx::Insets::TLBR(0, 4, 4, 4);
+constexpr int kErrorMessageRoundedCornerRadius = 12;
+constexpr gfx::Insets kErrorMessageButtonInsets =
+    gfx::Insets::TLBR(8, 10, 8, 16);
+constexpr gfx::Insets kErrorMessageLabelInsets = gfx::Insets::TLBR(8, 16, 8, 0);
+
 std::optional<int> GetYouTubeMusicIconResourceId() {
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   return IDR_YOUTUBE_MUSIC_ICON;
@@ -174,10 +181,7 @@
 
     if (soundscape_container_) {
       // Start downloading playlists for Soundscape.
-      sounds_controller->DownloadPlaylistsForType(
-          /*is_soundscape_type=*/true,
-          base::BindOnce(&FocusModeSoundsView::UpdateSoundsView,
-                         weak_factory_.GetWeakPtr()));
+      DownloadPlaylistsForType(/*is_soundscape_type=*/true);
     }
 
     if (youtube_music_container_) {
@@ -186,10 +190,7 @@
       sounds_controller->SetYouTubeMusicNoPremiumCallback(base::BindRepeating(
           &FocusModeSoundsView::ToggleYouTubeMusicAlternateView,
           weak_factory_.GetWeakPtr(), /*show=*/true));
-      sounds_controller->DownloadPlaylistsForType(
-          /*is_soundscape_type=*/false,
-          base::BindOnce(&FocusModeSoundsView::UpdateSoundsView,
-                         weak_factory_.GetWeakPtr()));
+      DownloadPlaylistsForType(/*is_soundscape_type=*/false);
     }
 
     if (should_show_soundscapes) {
@@ -209,6 +210,14 @@
       this);
 }
 
+void FocusModeSoundsView::Layout(PassKey) {
+  LayoutSuperclass<RoundedContainer>(this);
+  if (error_message_) {
+    error_message_->UpdateBoundsToContainer(GetLocalBounds(),
+                                            kErrorMessagePadding);
+  }
+}
+
 void FocusModeSoundsView::OnSelectedPlaylistChanged() {
   const auto& selected_playlist = FocusModeController::Get()
                                       ->focus_mode_sounds_controller()
@@ -243,6 +252,25 @@
   }
 }
 
+void FocusModeSoundsView::OnPlayerError() {
+  const auto& selected_playlist = FocusModeController::Get()
+                                      ->focus_mode_sounds_controller()
+                                      ->selected_playlist();
+  if (selected_playlist.empty()) {
+    LOG(WARNING) << "Media player error with no selected playlist";
+    return;
+  }
+
+  const bool is_soundscape_type =
+      selected_playlist.type == focus_mode_util::SoundType::kSoundscape;
+  ShowErrorMessageForType(
+      /*is_soundscape_type=*/is_soundscape_type,
+      is_soundscape_type
+          ? IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_FAILED_PLAYING_SOUNDS
+          : IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_YOUTUBE_MUSIC,
+      ErrorMessageToast::ButtonActionType::kDismiss);
+}
+
 void FocusModeSoundsView::UpdateSoundsView(bool is_soundscape_type) {
   auto* sounds_controller =
       FocusModeController::Get()->focus_mode_sounds_controller();
@@ -252,6 +280,10 @@
     }
     const auto& playlists = sounds_controller->soundscape_playlists();
     if (playlists.size() != kFocusModePlaylistViewsNum) {
+      ShowErrorMessageForType(
+          is_soundscape_type,
+          IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_FOCUS_SOUNDS,
+          ErrorMessageToast::ButtonActionType::kReload);
       return;
     }
     soundscape_container_->UpdateContents(playlists);
@@ -261,6 +293,12 @@
     }
     const auto& playlists = sounds_controller->youtube_music_playlists();
     if (playlists.size() != kFocusModePlaylistViewsNum) {
+      if (!youtube_music_container_->IsAlternateViewVisible()) {
+        ShowErrorMessageForType(
+            is_soundscape_type,
+            IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_DISCONNECTED_WITH_YOUTUBE_MUSIC,
+            ErrorMessageToast::ButtonActionType::kReload);
+      }
       return;
     }
     youtube_music_container_->UpdateContents(playlists);
@@ -355,6 +393,7 @@
 void FocusModeSoundsView::ToggleYouTubeMusicAlternateView(bool show) {
   CHECK(youtube_music_container_);
   youtube_music_container_->ShowAlternateView(show);
+  MaybeDismissErrorMessage();
 }
 
 void FocusModeSoundsView::OnSoundscapeButtonToggled() {
@@ -366,6 +405,7 @@
 }
 
 void FocusModeSoundsView::MayShowSoundscapeContainer(bool show) {
+  MaybeDismissErrorMessage();
   if (soundscape_container_) {
     soundscape_container_->SetVisible(show);
   }
@@ -379,6 +419,55 @@
   }
 }
 
+void FocusModeSoundsView::DownloadPlaylistsForType(bool is_soundscape_type) {
+  FocusModeController::Get()
+      ->focus_mode_sounds_controller()
+      ->DownloadPlaylistsForType(
+          is_soundscape_type,
+          base::BindOnce(&FocusModeSoundsView::UpdateSoundsView,
+                         weak_factory_.GetWeakPtr()));
+}
+
+void FocusModeSoundsView::MaybeDismissErrorMessage() {
+  if (!error_message_.get()) {
+    return;
+  }
+
+  RemoveChildViewT(std::exchange(error_message_, nullptr));
+}
+
+void FocusModeSoundsView::ShowErrorMessageForType(
+    bool is_soundscape_type,
+    const int message_id,
+    ErrorMessageToast::ButtonActionType type) {
+  MaybeDismissErrorMessage();
+
+  views::Button::PressedCallback callback;
+  switch (type) {
+    case ErrorMessageToast::ButtonActionType::kDismiss:
+      callback =
+          base::BindRepeating(&FocusModeSoundsView::MaybeDismissErrorMessage,
+                              weak_factory_.GetWeakPtr());
+      break;
+    case ErrorMessageToast::ButtonActionType::kReload:
+      callback =
+          base::BindRepeating(&FocusModeSoundsView::DownloadPlaylistsForType,
+                              weak_factory_.GetWeakPtr(), is_soundscape_type);
+      break;
+  }
+
+  error_message_ = AddChildView(std::make_unique<ErrorMessageToast>(
+      std::move(callback), l10n_util::GetStringUTF16(message_id), type,
+      cros_tokens::kCrosSysSystemBase));
+  error_message_->SetProperty(views::kViewIgnoredByLayoutKey, true);
+  error_message_->layer()->SetRoundedCornerRadius(
+      gfx::RoundedCornersF(kErrorMessageRoundedCornerRadius));
+  error_message_->error_message_label()->SetProperty(views::kMarginsKey,
+                                                     kErrorMessageLabelInsets);
+  error_message_->action_button()->SetProperty(views::kMarginsKey,
+                                               kErrorMessageButtonInsets);
+}
+
 BEGIN_METADATA(FocusModeSoundsView)
 END_METADATA
 
diff --git a/ash/system/focus_mode/sounds/focus_mode_sounds_view.h b/ash/system/focus_mode/sounds/focus_mode_sounds_view.h
index 9e1c00a..ac19d76f 100644
--- a/ash/system/focus_mode/sounds/focus_mode_sounds_view.h
+++ b/ash/system/focus_mode/sounds/focus_mode_sounds_view.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_FOCUS_MODE_SOUNDS_FOCUS_MODE_SOUNDS_VIEW_H_
 
 #include "ash/ash_export.h"
+#include "ash/style/error_message_toast.h"
 #include "ash/style/rounded_container.h"
 #include "ash/system/focus_mode/focus_mode_util.h"
 #include "ash/system/focus_mode/sounds/focus_mode_sounds_controller.h"
@@ -35,9 +36,13 @@
   FocusModeSoundsView& operator=(const FocusModeSoundsView&) = delete;
   ~FocusModeSoundsView() override;
 
+  // views::View:
+  void Layout(PassKey) override;
+
   // FocusModeSoundsController::Observer:
   void OnSelectedPlaylistChanged() override;
   void OnPlaylistStateChanged() override;
+  void OnPlayerError() override;
 
   std::pair<TabSliderButton*, SoundSectionView*> soundscape_views() const {
     return {soundscape_button_, soundscape_container_};
@@ -79,6 +84,13 @@
   // `youtube_music_container_`; also, we will update the a11y state for two
   // `TabSliderButton` based on which container is visible if they exists.
   void MayShowSoundscapeContainer(bool show);
+  // Loads the playlists based on `is_soundscape_type` into `SoundSectionView`.
+  void DownloadPlaylistsForType(bool is_soundscape_type);
+
+  void MaybeDismissErrorMessage();
+  void ShowErrorMessageForType(bool is_soundscape_type,
+                               const int message_id,
+                               ErrorMessageToast::ButtonActionType type);
 
   // The slider buttons on the sound view.
   raw_ptr<TabSliderButton> soundscape_button_ = nullptr;
@@ -88,6 +100,10 @@
   raw_ptr<SoundSectionView> soundscape_container_ = nullptr;
   raw_ptr<SoundSectionView> youtube_music_container_ = nullptr;
 
+  // An error toast will be shown at the bottom of this view if there is an
+  // error when loading playlists or when playing audio.
+  raw_ptr<ErrorMessageToast> error_message_ = nullptr;
+
   base::WeakPtrFactory<FocusModeSoundsView> weak_factory_{this};
 };
 
diff --git a/ash/system/focus_mode/sounds/sound_section_view.cc b/ash/system/focus_mode/sounds/sound_section_view.cc
index 5c376ab..1778242 100644
--- a/ash/system/focus_mode/sounds/sound_section_view.cc
+++ b/ash/system/focus_mode/sounds/sound_section_view.cc
@@ -77,6 +77,10 @@
   alternate_view_ = AddChildView(std::move(alternate_view));
 }
 
+bool SoundSectionView::IsAlternateViewVisible() const {
+  return alternate_view_ && alternate_view_->GetVisible();
+}
+
 void SoundSectionView::UpdateStateForSelectedPlaylist(
     const focus_mode_util::SelectedPlaylist& selected_playlist) {
   for (auto* playlist_view : playlist_view_list_) {
diff --git a/ash/system/focus_mode/sounds/sound_section_view.h b/ash/system/focus_mode/sounds/sound_section_view.h
index a3072e1..a5d1c19 100644
--- a/ash/system/focus_mode/sounds/sound_section_view.h
+++ b/ash/system/focus_mode/sounds/sound_section_view.h
@@ -47,6 +47,7 @@
   // instead.
   void ShowAlternateView(bool show_alternate_view);
   void SetAlternateView(std::unique_ptr<views::BoxLayoutView> alternate_view);
+  bool IsAlternateViewVisible() const;
 
   // Updates the state of `PlaylistView` for the newly `selected_playlist` and
   // reset the state of `PlaylistView` for unselected playlists.
diff --git a/ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc b/ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc
index 245c37961..1158ece 100644
--- a/ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc
+++ b/ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc
@@ -327,27 +327,24 @@
 TEST_F(InputDeviceSettingsNotificationControllerTest,
        ShowPeripheralSettingsOnCustomizationNotificationClick) {
   NotifyMouseIsCustomizable(kMouse1);
-  message_center()->ClickOnNotification("peripheral_customization_mouse_1");
+  message_center()->ClickOnNotification("welcome_experience_1");
   EXPECT_EQ(GetSystemTrayClient()->show_mouse_settings_count(), 1);
 
   NotifyGraphicsTabletIsCustomizable(kGraphicsTablet2);
-  message_center()->ClickOnNotification(
-      "peripheral_customization_graphics_tablet_2");
+  message_center()->ClickOnNotification("welcome_experience_2");
   EXPECT_EQ(GetSystemTrayClient()->show_graphics_tablet_settings_count(), 1);
 }
 
 TEST_F(InputDeviceSettingsNotificationControllerTest,
        ShowPeripheralSettingsOnCustomizationNotificationButtonClick) {
   NotifyMouseIsCustomizable(kMouse1);
-  message_center()->ClickOnNotificationButton(
-      "peripheral_customization_mouse_1",
-      /*button_index=*/0);
+  message_center()->ClickOnNotificationButton("welcome_experience_1",
+                                              /*button_index=*/0);
   EXPECT_EQ(GetSystemTrayClient()->show_mouse_settings_count(), 1);
 
   NotifyGraphicsTabletIsCustomizable(kGraphicsTablet2);
-  message_center()->ClickOnNotificationButton(
-      "peripheral_customization_graphics_tablet_2",
-      /*button_index=*/0);
+  message_center()->ClickOnNotificationButton("welcome_experience_2",
+                                              /*button_index=*/0);
   EXPECT_EQ(GetSystemTrayClient()->show_graphics_tablet_settings_count(), 1);
 }
 
@@ -470,31 +467,33 @@
 
   PrefService* prefs =
       Shell::Get()->session_controller()->GetActivePrefService();
-  EXPECT_TRUE(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).empty());
-  NotifyMouseFirstTimeConnected(*mojom_mouse);
-  EXPECT_EQ(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).size(), 1u);
   EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kPeripheralNotificationMiceSeen),
+      prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).empty());
+  NotifyMouseFirstTimeConnected(*mojom_mouse);
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            1u);
+  EXPECT_TRUE(
+      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
                      base::Value("0001:0001")));
   NotifyMouseFirstTimeConnected(*mojom_mouse);
-  EXPECT_EQ(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).size(), 1u);
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            1u);
   EXPECT_EQ(expected_notification_count++,
             message_center()->NotificationCount());
-  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
-      "peripheral_customization_mouse_1"));
-
+  EXPECT_TRUE(
+      message_center()->FindVisibleNotificationById("welcome_experience_1"));
   mojom_mouse->id = 2;
   mojom_mouse->device_key = "0001:0002";
 
   NotifyMouseFirstTimeConnected(*mojom_mouse);
-  EXPECT_EQ(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).size(), 2u);
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            2u);
   EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kPeripheralNotificationMiceSeen),
+      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
                      base::Value("0001:0002")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
-      "peripheral_customization_mouse_2"));
-
+  EXPECT_TRUE(
+      message_center()->FindVisibleNotificationById("welcome_experience_2"));
   mojom_mouse->id = 3;
   mojom_mouse->device_key = "0001:0003";
   mojom_mouse->settings->button_remappings.push_back(
@@ -504,13 +503,14 @@
               mojom::CustomizableButton::kBack),
           /*remapping_action=*/nullptr));
   NotifyMouseFirstTimeConnected(*mojom_mouse);
-  EXPECT_EQ(prefs->GetList(prefs::kPeripheralNotificationMiceSeen).size(), 3u);
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            3u);
   EXPECT_TRUE(
-      base::Contains(prefs->GetList(prefs::kPeripheralNotificationMiceSeen),
+      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
                      base::Value("0001:0003")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
-      "peripheral_customization_mouse_3"));
+  EXPECT_FALSE(
+      message_center()->FindVisibleNotificationById("welcome_experience_3"));
 }
 
 TEST_F(InputDeviceSettingsNotificationControllerTest,
@@ -522,40 +522,33 @@
   mojom_graphics_tablet->settings = mojom::GraphicsTabletSettings::New();
   PrefService* prefs =
       Shell::Get()->session_controller()->GetActivePrefService();
-
-  EXPECT_TRUE(prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen)
-                  .empty());
+  EXPECT_TRUE(
+      prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).empty());
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
-      1u);
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            1u);
   EXPECT_EQ(expected_notification_count++,
             message_center()->NotificationCount());
-  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
-      "peripheral_customization_graphics_tablet_1"));
-
-  EXPECT_TRUE(base::Contains(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen),
-      base::Value("0002:0001")));
+  EXPECT_TRUE(
+      message_center()->FindVisibleNotificationById("welcome_experience_1"));
+  EXPECT_TRUE(
+      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
+                     base::Value("0002:0001")));
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
-      1u);
-
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            1u);
   mojom_graphics_tablet->id = 2;
   mojom_graphics_tablet->device_key = "0002:0002";
 
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
-      2u);
-  EXPECT_TRUE(base::Contains(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen),
-      base::Value("0002:0002")));
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            2u);
+  EXPECT_TRUE(
+      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
+                     base::Value("0002:0002")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
-      "peripheral_customization_graphics_tablet_2"));
-
+  EXPECT_TRUE(
+      message_center()->FindVisibleNotificationById("welcome_experience_2"));
   mojom_graphics_tablet->id = 3;
   mojom_graphics_tablet->device_key = "0002:0003";
   mojom_graphics_tablet->settings->pen_button_remappings.push_back(
@@ -566,16 +559,14 @@
           /*remapping_action=*/nullptr));
 
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
-      3u);
-  EXPECT_TRUE(base::Contains(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen),
-      base::Value("0002:0003")));
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            3u);
+  EXPECT_TRUE(
+      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
+                     base::Value("0002:0003")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
-      "peripheral_customization_graphics_tablet_3"));
-
+  EXPECT_FALSE(
+      message_center()->FindVisibleNotificationById("welcome_experience_3"));
   mojom_graphics_tablet->id = 4;
   mojom_graphics_tablet->device_key = "0002:0004";
   mojom_graphics_tablet->settings->pen_button_remappings.clear();
@@ -587,15 +578,14 @@
           /*remapping_action=*/nullptr));
 
   NotifyGraphicsTabletFirstTimeConnected(*mojom_graphics_tablet);
-  EXPECT_EQ(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen).size(),
-      4u);
-  EXPECT_TRUE(base::Contains(
-      prefs->GetList(prefs::kPeripheralNotificationGraphicsTabletsSeen),
-      base::Value("0002:0004")));
+  EXPECT_EQ(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen).size(),
+            4u);
+  EXPECT_TRUE(
+      base::Contains(prefs->GetList(prefs::kWelcomeExperienceNotificationSeen),
+                     base::Value("0002:0004")));
   EXPECT_EQ(expected_notification_count, message_center()->NotificationCount());
-  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
-      "peripheral_customization_graphics_tablet_4"));
+  EXPECT_FALSE(
+      message_center()->FindVisibleNotificationById("welcome_experience_4"));
 }
 
 TEST_F(InputDeviceSettingsNotificationControllerTest,
@@ -924,8 +914,8 @@
   NotifyMouseFirstTimeConnected(*mojom_mouse);
   EXPECT_EQ(expected_notification_count++,
             message_center()->NotificationCount());
-  const auto* notification = message_center()->FindVisibleNotificationById(
-      "peripheral_customization_mouse_1");
+  const auto* notification =
+      message_center()->FindVisibleNotificationById("welcome_experience_1");
   ASSERT_TRUE(notification);
   EXPECT_EQ(
       l10n_util::GetStringFUTF16(
@@ -949,8 +939,8 @@
       gfx::test::CreateImageSkia(/*width=*/300, /*height=*/300, SK_ColorRED));
   EXPECT_EQ(expected_notification_count++,
             message_center()->NotificationCount());
-  const auto* notification = message_center()->FindVisibleNotificationById(
-      "peripheral_customization_mouse_1");
+  const auto* notification =
+      message_center()->FindVisibleNotificationById("welcome_experience_1");
   EXPECT_FALSE(notification->image().IsEmpty());
 }
 
diff --git a/ash/system/keyboard_brightness/keyboard_brightness_controller.cc b/ash/system/keyboard_brightness/keyboard_brightness_controller.cc
index 8f2f56f3..9c74fd9b 100644
--- a/ash/system/keyboard_brightness/keyboard_brightness_controller.cc
+++ b/ash/system/keyboard_brightness/keyboard_brightness_controller.cc
@@ -329,7 +329,15 @@
 void KeyboardBrightnessController::OnFocusPod(const AccountId& account_id) {
   active_account_id_ = account_id;
 
-  if (features::IsKeyboardBacklightControlInSettingsEnabled()) {
+  if (!features::IsKeyboardBacklightControlInSettingsEnabled()) {
+    return;
+  }
+
+  session_manager::SessionState session_state =
+      Shell::Get()->session_controller()->GetSessionState();
+  if (session_state == session_manager::SessionState::LOGIN_PRIMARY ||
+      session_state == session_manager::SessionState::LOGIN_SECONDARY) {
+    // Restore brightness settings only when device reboots.
     RestoreKeyboardBrightnessSettings(account_id);
   }
 }
diff --git a/ash/system/keyboard_brightness/keyboard_brightness_controller_unittest.cc b/ash/system/keyboard_brightness/keyboard_brightness_controller_unittest.cc
index aa1d319a..f231a3ce 100644
--- a/ash/system/keyboard_brightness/keyboard_brightness_controller_unittest.cc
+++ b/ash/system/keyboard_brightness/keyboard_brightness_controller_unittest.cc
@@ -953,6 +953,8 @@
                                                            account_id));
 
   // Simulate reboot, and log in again.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   known_user.SetPath(account_id, prefs::kKeyboardBrightnessPercent,
                      std::make_optional<base::Value>(30.0));
   login_data_dispatcher()->NotifyFocusPod(account_id);
@@ -963,6 +965,8 @@
   ExpectKeyboardBrightnessPercent(30.0);
 
   // Simulate reboot, and log in the third time.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   login_data_dispatcher()->NotifyFocusPod(account_id);
 
   // ALS and brightness should remain the same as last reboot.
@@ -1014,6 +1018,8 @@
                                                            account_id));
 
   // Simulate reboot, and log in again.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   known_user.SetPath(account_id, prefs::kKeyboardBrightnessPercent,
                      std::make_optional<base::Value>(30.0));
   login_data_dispatcher()->NotifyFocusPod(account_id);
@@ -1022,6 +1028,8 @@
   ExpectKeyboardAmbientLightSensorEnabled(true);
 
   // Simulate reboot, and log in the third time.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
   login_data_dispatcher()->NotifyFocusPod(account_id);
 
   // ALS and should remain the same as last reboot.
@@ -1029,6 +1037,46 @@
 }
 
 TEST_F(KeyboardBrightnessControllerTest,
+       BrightnessSettingsUnchanged_DeviceLocked) {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kEnableKeyboardBacklightControlInSettings);
+
+  // Set initial ALS and keyboard brightness.
+  power_manager::SetAmbientLightSensorEnabledRequest request;
+  request.set_sensor_enabled(true);
+  power_manager_client()->SetKeyboardAmbientLightSensorEnabled(request);
+  power_manager_client()->set_keyboard_brightness_percent(
+      kInitialKeyboardBrightness);
+
+  // Log in
+  ClearLogin();
+  AccountId account_id = AccountId::FromUserEmail(kUserEmail);
+  user_manager::KnownUser known_user(local_state());
+  SimulateUserLogin(kUserEmail);
+
+  // Disable ALS using the brightness key.
+  SetKeyboardAmbientLightSensorEnabled(
+      false,
+      power_manager::AmbientLightSensorChange_Cause_BRIGHTNESS_USER_REQUEST);
+
+  // Current status: Als is turned off, and current brightness is
+  // kInitialBrightness.
+  ExpectKeyboardAmbientLightSensorEnabled(false);
+  ExpectKeyboardBrightnessPercent(kInitialKeyboardBrightness);
+
+  // Simulate device lock and re-login.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::ACTIVE);
+  login_data_dispatcher()->NotifyFocusPod(account_id);
+
+  // Als should not be re-enabled, although it was not previously disabled from
+  // settings app. The brightness percent should still be
+  // kInitialKeyboardBrightness.
+  ExpectKeyboardAmbientLightSensorEnabled(false);
+  ExpectKeyboardBrightnessPercent(kInitialKeyboardBrightness);
+}
+
+TEST_F(KeyboardBrightnessControllerTest,
        RecordStartupKeyboardAmbientLightSensorStatus) {
   scoped_feature_list_.InitAndEnableFeature(
       features::kEnableKeyboardBacklightControlInSettings);
diff --git a/ash/system/network/managed_sim_lock_notifier_unittest.cc b/ash/system/network/managed_sim_lock_notifier_unittest.cc
index 386b92b7..0c72f09 100644
--- a/ash/system/network/managed_sim_lock_notifier_unittest.cc
+++ b/ash/system/network/managed_sim_lock_notifier_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "chromeos/ash/components/network/cellular_metrics_logger.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
@@ -162,10 +163,13 @@
   std::unique_ptr<network_config::CrosNetworkConfigTestHelper>
       network_config_helper_;
   std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::HistogramTester histogram_tester_;
 };
 
 TEST_F(ManagedSimLockNotifierTest, PolicyChanged) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   AddCellularDevice();
   AddCellularService();
   EXPECT_FALSE(GetManagedSimLockNotification());
@@ -181,6 +185,8 @@
 }
 
 TEST_F(ManagedSimLockNotifierTest, NewActiveSession) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   AddCellularDevice();
   AddCellularService();
   SetCellularSimLockEnabled(true);
@@ -233,6 +239,8 @@
 }
 
 TEST_F(ManagedSimLockNotifierTest, HideNotificationOnLockDisabled) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   AddCellularDevice();
   AddCellularService();
   SetCellularSimLockEnabled(true);
@@ -246,6 +254,8 @@
 }
 
 TEST_F(ManagedSimLockNotifierTest, PrimarySimIccidChanged) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   AddCellularDevice();
   AddCellularService();
   SetCellularSimLockEnabled(true);
@@ -279,6 +289,8 @@
 }
 
 TEST_F(ManagedSimLockNotifierTest, NotificationOnCellularOnOrOff) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   base::HistogramTester histograms;
 
   AddCellularDevice();
@@ -301,6 +313,8 @@
 }
 
 TEST_F(ManagedSimLockNotifierTest, NotificationClicked) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   base::HistogramTester histograms;
 
   AddCellularDevice();
@@ -326,6 +340,8 @@
 }
 
 TEST_F(ManagedSimLockNotifierTest, NotificationDismissedByUser) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   base::HistogramTester histograms;
 
   AddCellularDevice();
@@ -349,6 +365,8 @@
 }
 
 TEST_F(ManagedSimLockNotifierTest, SIMLockTypeMetrics) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   base::HistogramTester histograms;
 
   AddCellularDevice();
diff --git a/ash/system/video_conference/bubble/bubble_view.cc b/ash/system/video_conference/bubble/bubble_view.cc
index 9fdd545..e805dc4 100644
--- a/ash/system/video_conference/bubble/bubble_view.cc
+++ b/ash/system/video_conference/bubble/bubble_view.cc
@@ -192,7 +192,7 @@
         std::make_unique<SetValueEffectsView>(controller_));
   }
 
-  if (GetCameraEffectsController()->is_eligible_for_background_replace()) {
+  if (GetCameraEffectsController()->IsEligibleForBackgroundReplace()) {
     set_camera_background_view_ = scroll_contents_view->AddChildView(
         std::make_unique<SetCameraBackgroundView>(this, controller_.get()));
   }
diff --git a/ash/system/video_conference/bubble/set_camera_background_view.cc b/ash/system/video_conference/bubble/set_camera_background_view.cc
index badab7a..32c38df 100644
--- a/ash/system/video_conference/bubble/set_camera_background_view.cc
+++ b/ash/system/video_conference/bubble/set_camera_background_view.cc
@@ -101,12 +101,6 @@
   return Shell::Get()->camera_effects_controller();
 }
 
-bool IsVcBackgroundAllowedByEnterprise() {
-  auto* controller = Shell::Get()->session_controller();
-  return std::get<1>(
-      controller->IsEligibleForSeaPen(controller->GetActiveAccountId()));
-}
-
 // Returns a gradient lottie animation defined in the resource file for the
 // `Create with AI` button.
 std::unique_ptr<lottie::Animation> GetGradientAnimation(
@@ -463,7 +457,7 @@
   SetID(BubbleViewID::kSetCameraBackgroundView);
   SetVisible(
       GetCameraEffectsController()->GetCameraEffects()->replace_enabled &&
-      IsVcBackgroundAllowedByEnterprise());
+      GetCameraEffectsController()->IsVcBackgroundAllowedByEnterprise());
 
   // `SetCameraBackgroundView` has 2+ children, we want to stack them
   // vertically.
diff --git a/ash/system/video_conference/effects/video_conference_tray_effects_manager.cc b/ash/system/video_conference/effects/video_conference_tray_effects_manager.cc
index 86d87c2..bcea538 100644
--- a/ash/system/video_conference/effects/video_conference_tray_effects_manager.cc
+++ b/ash/system/video_conference/effects/video_conference_tray_effects_manager.cc
@@ -159,6 +159,12 @@
   }
 }
 
+void VideoConferenceTrayEffectsManager::NotifyVideoConferenceBubbleOpened() {
+  for (auto& observer : observers_) {
+    observer.OnVideoConferenceBubbleOpened();
+  }
+}
+
 void VideoConferenceTrayEffectsManager::RecordInitialStates() {
   for (ash::VcEffectsDelegate* delegate : effect_delegates_) {
     delegate->RecordInitialStates();
diff --git a/ash/system/video_conference/effects/video_conference_tray_effects_manager.h b/ash/system/video_conference/effects/video_conference_tray_effects_manager.h
index 69633b6..20f29f4 100644
--- a/ash/system/video_conference/effects/video_conference_tray_effects_manager.h
+++ b/ash/system/video_conference/effects/video_conference_tray_effects_manager.h
@@ -47,6 +47,9 @@
     // Called when an effect changes. Currently, only observes
     // `kStudioLook` effect.
     virtual void OnEffectChanged(VcEffectId effect_id, bool is_on) {}
+
+    // Called when the video conference bubble is opened.
+    virtual void OnVideoConferenceBubbleOpened() {}
   };
 
   // Adds/removes `VideoConferenceTrayEffectsManager::Observer`.
@@ -99,6 +102,9 @@
   // Notifies all observers about effect state changed.
   void NotifyEffectChanged(VcEffectId effect_id, bool is_on);
 
+  // Notifies all observers about the video conference bubble opened.
+  void NotifyVideoConferenceBubbleOpened();
+
   // Records the current state of all effects to metrics.
   void RecordInitialStates();
 
diff --git a/ash/system/video_conference/video_conference_tray.cc b/ash/system/video_conference/video_conference_tray.cc
index 845a042d..c04f7abd 100644
--- a/ash/system/video_conference/video_conference_tray.cc
+++ b/ash/system/video_conference/video_conference_tray.cc
@@ -15,6 +15,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
 #include "ash/style/icon_button.h"
+#include "ash/system/camera/camera_effects_controller.h"
 #include "ash/system/privacy/screen_security_controller.h"
 #include "ash/system/system_notification_controller.h"
 #include "ash/system/tray/tray_background_view.h"
@@ -493,6 +494,10 @@
 
   VideoConferenceTrayController::Get()->CloseAllVcNudges();
 
+  VideoConferenceTrayController::Get()
+      ->GetEffectsManager()
+      .NotifyVideoConferenceBubbleOpened();
+
   // If we are already in the process of getting the media apps, we don't need
   // to get it again.
   if (!getting_media_apps_) {
diff --git a/ash/webui/boca_ui/boca_app_page_handler.cc b/ash/webui/boca_ui/boca_app_page_handler.cc
index a10fdf0..5d05e9a9 100644
--- a/ash/webui/boca_ui/boca_app_page_handler.cc
+++ b/ash/webui/boca_ui/boca_app_page_handler.cc
@@ -10,10 +10,12 @@
 #include "ash/shell.h"
 #include "ash/webui/boca_ui/boca_ui.h"
 #include "ash/webui/boca_ui/mojom/boca.mojom-shared.h"
+#include "ash/webui/boca_ui/mojom/boca.mojom.h"
 #include "ash/webui/boca_ui/provider/classroom_page_handler_impl.h"
 #include "ash/webui/boca_ui/provider/tab_info_collector.h"
 #include "base/time/time.h"
 #include "chromeos/ash/components/boca/boca_app_client.h"
+#include "chromeos/ash/components/boca/boca_session_util.h"
 #include "chromeos/ash/components/boca/proto/bundle.pb.h"
 #include "chromeos/ash/components/boca/proto/session.pb.h"
 #include "chromeos/ash/components/boca/session_api/create_session_request.h"
@@ -127,6 +129,66 @@
   NotifyLocalConfigUpdate(std::move(config));
 }
 
+void BocaAppHandler::GetSession(GetSessionCallback callback) {
+  auto get_session_request = std::make_unique<GetSessionRequest>(
+      session_client_impl_->sender(), user_identity_.GetGaiaId(),
+      base::BindOnce(
+          [](GetSessionCallback callback,
+             base::expected<std::unique_ptr<::boca::Session>,
+                            google_apis::ApiErrorCode> result) {
+            // TODO(b/358476060):Potentially parse error code to UI;
+            if (!result.has_value()) {
+              std::move(callback).Run(nullptr);
+              return;
+            }
+            auto session = std::move(result.value());
+            std::vector<mojom::IdentityPtr> students;
+            for (auto student : GetStudentGroupsSafe(session.get())) {
+              students.push_back(mojom::Identity::New(
+                  student.gaia_id(), student.full_name(), student.email()));
+            }
+
+            auto caption_config = mojom::CaptionConfig::New();
+            if (GetSessionConfigSafe(session.get()).has_captions_config()) {
+              auto session_caption_config =
+                  GetSessionConfigSafe(session.get()).captions_config();
+              caption_config->caption_enabled =
+                  session_caption_config.captions_enabled();
+              caption_config->transcription_enabled =
+                  session_caption_config.translations_enabled();
+            }
+
+            mojom::OnTaskConfigPtr on_task_config;
+            if (GetSessionConfigSafe(session.get()).has_on_task_config()) {
+              auto session_on_task_config =
+                  GetSessionConfigSafe(session.get()).on_task_config();
+              std::vector<mojom::ControlledTabPtr> tabs;
+              for (auto tab :
+                   session_on_task_config.active_bundle().content_configs()) {
+                tabs.push_back(mojom::ControlledTab::New(
+                    mojom::TabInfo::New(tab.title(), GURL(tab.url()),
+                                        tab.favicon_url()),
+                    mojom::NavigationType(
+                        tab.locked_navigation_options().navigation_type())));
+              }
+              on_task_config = mojom::OnTaskConfig::New(
+                  session_on_task_config.active_bundle().locked(),
+                  std::move(tabs));
+            }
+
+            auto config = mojom::Config::New(
+                // Nanos are not used throughout session lifecycle so it's
+                // safe to only parse seconds.
+                base::Seconds(session->duration().seconds()),
+                std::move(students), std::move(on_task_config),
+                std::move(caption_config));
+
+            std::move(callback).Run(std::move(config));
+          },
+          std::move(callback)));
+  session_client_impl_->GetSession(std::move(get_session_request));
+}
+
 void BocaAppHandler::NotifyLocalConfigUpdate(mojom::ConfigPtr config) {
   if (auto caption_config = std::move(config->caption_config)) {
     ::boca::CaptionsConfig local_caption_config;
diff --git a/ash/webui/boca_ui/boca_app_page_handler.h b/ash/webui/boca_ui/boca_app_page_handler.h
index 5d50706..f50e6f3 100644
--- a/ash/webui/boca_ui/boca_app_page_handler.h
+++ b/ash/webui/boca_ui/boca_app_page_handler.h
@@ -42,6 +42,7 @@
                     ListStudentsCallback callback) override;
   void CreateSession(mojom::ConfigPtr config,
                      CreateSessionCallback callback) override;
+  void GetSession(GetSessionCallback callback) override;
 
   void NotifyLocalConfigUpdate(mojom::ConfigPtr config);
 
diff --git a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
index c9621869..79d71ea8 100644
--- a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
+++ b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
@@ -17,6 +17,7 @@
 #include "chromeos/ash/components/boca/proto/bundle.pb.h"
 #include "chromeos/ash/components/boca/proto/roster.pb.h"
 #include "chromeos/ash/components/boca/proto/session.pb.h"
+#include "chromeos/ash/components/boca/session_api/constants.h"
 #include "chromeos/ash/components/boca/session_api/create_session_request.h"
 #include "chromeos/ash/components/boca/session_api/session_client_impl.h"
 #include "components/account_id/account_id.h"
@@ -39,6 +40,7 @@
 using ::testing::WithArg;
 
 namespace ash::boca {
+namespace {
 constexpr char kGaiaId[] = "123";
 constexpr char kUserEmail[] = "cat@gmail.com";
 
@@ -51,6 +53,10 @@
               CreateSession,
               (std::unique_ptr<CreateSessionRequest>),
               (override));
+  MOCK_METHOD(void,
+              GetSession,
+              (std::unique_ptr<GetSessionRequest>),
+              (override));
 };
 
 class MockBocaAppClient : public BocaAppClient {
@@ -285,4 +291,96 @@
   ASSERT_TRUE(future_1.Wait());
   EXPECT_TRUE(future_1.Get());
 }
+
+TEST_F(BocaAppPageHandlerTest, GetSessionWithFullInputTest) {
+  // Page handler callback.
+  base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
+                                        google_apis::ApiErrorCode>>
+      future;
+  // API callback.
+  base::test::TestFuture<mojom::ConfigPtr> future_1;
+
+  GetSessionRequest request(nullptr, kGaiaId, future.GetCallback());
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(WithArg<0>(Invoke([&](auto request) {
+        auto session = std::make_unique<::boca::Session>();
+        session->mutable_duration()->set_seconds(120);
+
+        auto* student_groups_1 =
+            session->mutable_roster()->mutable_student_groups()->Add();
+        student_groups_1->set_title(kMainStudentGroupName);
+        auto* student = student_groups_1->mutable_students()->Add();
+        student->set_email("dog@email.com");
+        student->set_full_name("dog");
+        student->set_gaia_id("123");
+
+        ::boca::SessionConfig session_config;
+        auto* caption_config_1 = session_config.mutable_captions_config();
+
+        caption_config_1->set_captions_enabled(true);
+        caption_config_1->set_translations_enabled(true);
+
+        auto* active_bundle =
+            session_config.mutable_on_task_config()->mutable_active_bundle();
+        active_bundle->set_locked(true);
+        auto* content = active_bundle->mutable_content_configs()->Add();
+        content->set_url("http://google.com/");
+        content->set_favicon_url("data/image");
+        content->set_title("google");
+        content->mutable_locked_navigation_options()->set_navigation_type(
+            ::boca::LockedNavigationOptions_NavigationType_OPEN_NAVIGATION);
+
+        (*session->mutable_student_group_configs())[kMainStudentGroupName] =
+            std::move(session_config);
+        request->callback().Run(std::move(session));
+      })));
+
+  boca_app_handler_->GetSession(future_1.GetCallback());
+
+  auto result = future_1.Take();
+
+  EXPECT_EQ(120, result->session_duration.InSeconds());
+
+  EXPECT_EQ(true, result->caption_config->caption_enabled);
+  EXPECT_EQ(true, result->caption_config->transcription_enabled);
+
+  ASSERT_EQ(1u, result->students.size());
+
+  EXPECT_EQ("dog", result->students[0]->name);
+  EXPECT_EQ("123", result->students[0]->id);
+  EXPECT_EQ("dog@email.com", result->students[0]->email);
+
+  ASSERT_EQ(1u, result->on_task_config->tabs.size());
+  ASSERT_TRUE(result->on_task_config->is_locked);
+  EXPECT_EQ(mojom::NavigationType::kOpen,
+            result->on_task_config->tabs[0]->navigation_type);
+  EXPECT_EQ("http://google.com/",
+            result->on_task_config->tabs[0]->tab->url.spec());
+  EXPECT_EQ("google", result->on_task_config->tabs[0]->tab->title);
+  EXPECT_EQ("data/image", result->on_task_config->tabs[0]->tab->favicon);
+}
+
+TEST_F(BocaAppPageHandlerTest, GetSessionWithPartialInputTest) {
+  // Page handler callback.
+  base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
+                                        google_apis::ApiErrorCode>>
+      future;
+  // API callback.
+  base::test::TestFuture<mojom::ConfigPtr> future_1;
+
+  GetSessionRequest request(nullptr, kGaiaId, future.GetCallback());
+  EXPECT_CALL(*session_client_impl(), GetSession(_))
+      .WillOnce(WithArg<0>(Invoke([&](auto request) {
+        auto session = std::make_unique<::boca::Session>();
+        session->mutable_duration()->set_seconds(120);
+        request->callback().Run(std::move(session));
+      })));
+
+  boca_app_handler_->GetSession(future_1.GetCallback());
+
+  auto result = future_1.Take();
+  EXPECT_EQ(120, result->session_duration.InSeconds());
+}
+
+}  // namespace
 }  // namespace ash::boca
diff --git a/ash/webui/boca_ui/mojom/boca.mojom b/ash/webui/boca_ui/mojom/boca.mojom
index 50a993e8..cadb565 100644
--- a/ash/webui/boca_ui/mojom/boca.mojom
+++ b/ash/webui/boca_ui/mojom/boca.mojom
@@ -92,7 +92,7 @@
   // non-increasing order for most recent tab's last
   // access timestamp
   GetWindowsTabsList() => (array<Window> window_list);
-    // Fetch a list of courses for the current teacher.
+  // Fetch a list of courses for the current teacher.
   ListCourses() => (array<Course> courses);
   // Requested by the Boca SWA to fetch a list of students for a given
   // course. Must be called on a course_id received from the most recent
@@ -100,6 +100,8 @@
   ListStudents(string course_id) => (array<Identity> students);
   // Create a session with `config`.
   CreateSession(Config config) => (bool success);
+  // Retrieves the current session.
+  GetSession() => (Config config);
 };
 
 // Implemented by renderer process
diff --git a/ash/webui/focus_mode/focus_mode_ui.cc b/ash/webui/focus_mode/focus_mode_ui.cc
index c1c883f..fb40a5f 100644
--- a/ash/webui/focus_mode/focus_mode_ui.cc
+++ b/ash/webui/focus_mode/focus_mode_ui.cc
@@ -172,6 +172,12 @@
         base::Time::Now());
   }
 
+  void ReportPlayerError() override {
+    if (auto* controller = FocusModeController::Get()) {
+      controller->focus_mode_sounds_controller()->ReportPlayerError();
+    }
+  }
+
   void BindInterface(
       mojo::PendingReceiver<focus_mode::mojom::TrackProvider> receiver) {
     receiver_.reset();
diff --git a/ash/webui/focus_mode/mojom/focus_mode.mojom b/ash/webui/focus_mode/mojom/focus_mode.mojom
index 85b671f..510e560 100644
--- a/ash/webui/focus_mode/mojom/focus_mode.mojom
+++ b/ash/webui/focus_mode/mojom/focus_mode.mojom
@@ -77,4 +77,7 @@
 
   // Used by the player to report playback.
   ReportPlayback(PlaybackData data);
+
+  // Used by the player to report an error.
+  ReportPlayerError();
 };
diff --git a/ash/webui/focus_mode/resources/app.ts b/ash/webui/focus_mode/resources/app.ts
index 36e189c..570f0ea 100644
--- a/ash/webui/focus_mode/resources/app.ts
+++ b/ash/webui/focus_mode/resources/app.ts
@@ -131,6 +131,10 @@
       typeof data.initial == 'boolean');
 }
 
+function isMediaErrorEventData(data: any): boolean {
+  return isEventData(data) && data.cmd == 'mediaErrorEvent';
+}
+
 function getProvider(): TrackProviderInterface {
   if (!providerInstance) {
     providerInstance = TrackProvider.getRemote();
@@ -142,6 +146,7 @@
 function postPlayRequest(track: TrackDefinition) {
   if (!track.mediaUrl.url) {
     // If there is no valid URL, then there's no point in continuing.
+    getProvider().reportPlayerError();
     return;
   }
 
@@ -240,6 +245,8 @@
         position: data.position,
         initial: data.initial,
       });
+    } else if (isMediaErrorEventData(data)) {
+      getProvider().reportPlayerError();
     }
   }
 });
diff --git a/ash/webui/focus_mode/untrusted_resources/player.ts b/ash/webui/focus_mode/untrusted_resources/player.ts
index 1e85dab..3ee77f9d 100644
--- a/ash/webui/focus_mode/untrusted_resources/player.ts
+++ b/ash/webui/focus_mode/untrusted_resources/player.ts
@@ -35,6 +35,10 @@
   parent.postMessage({cmd: 'gettrack'}, TRUSTED_ORIGIN);
 }
 
+function mediaErrorEvent() {
+  parent.postMessage({cmd: 'mediaErrorEvent'}, TRUSTED_ORIGIN);
+}
+
 interface PlaybackStatus {
   state: string;
   position: number;
@@ -102,6 +106,20 @@
     sendTrackRequest();
   });
 
+  getPlayerElement().addEventListener('error', () => {
+    const mediaError = getPlayerElement().error;
+    switch (mediaError?.code) {
+      case MediaError.MEDIA_ERR_ABORTED:
+      case MediaError.MEDIA_ERR_DECODE:
+      case MediaError.MEDIA_ERR_NETWORK:
+      case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
+        mediaErrorEvent();
+        break;
+      default:
+        break;
+    }
+  });
+
   // Registering this makes the "next track" button show up in the media
   // controls. We do not support going to the previous track.
   navigator.mediaSession.setActionHandler('nexttrack', () => {
diff --git a/ash/webui/help_app_ui/help_app_page_handler.cc b/ash/webui/help_app_ui/help_app_page_handler.cc
index 7da27ef4..bac2ace 100644
--- a/ash/webui/help_app_ui/help_app_page_handler.cc
+++ b/ash/webui/help_app_ui/help_app_page_handler.cc
@@ -76,4 +76,9 @@
   }
 }
 
+void HelpAppPageHandler::OpenSettings(
+    help_app::mojom::SettingsComponent component) {
+  help_app_ui_->delegate()->OpenSettings(component);
+}
+
 }  // namespace ash
diff --git a/ash/webui/help_app_ui/help_app_page_handler.h b/ash/webui/help_app_ui/help_app_page_handler.h
index 2d3295e57..e4c8864 100644
--- a/ash/webui/help_app_ui/help_app_page_handler.h
+++ b/ash/webui/help_app_ui/help_app_page_handler.h
@@ -41,6 +41,7 @@
   void MaybeShowReleaseNotesNotification() override;
   void GetDeviceInfo(GetDeviceInfoCallback callback) override;
   void OpenUrlInBrowserAndTriggerInstallDialog(const GURL& url) override;
+  void OpenSettings(help_app::mojom::SettingsComponent component) override;
 
  private:
   mojo::Receiver<help_app::mojom::PageHandler> receiver_;
diff --git a/ash/webui/help_app_ui/help_app_ui.mojom b/ash/webui/help_app_ui/help_app_ui.mojom
index e67970d..19d5aeb 100644
--- a/ash/webui/help_app_ui/help_app_ui.mojom
+++ b/ash/webui/help_app_ui/help_app_ui.mojom
@@ -41,6 +41,16 @@
   OPEN_FILE_MANAGER = 10,
 };
 
+/**
+ * Specifies an os settings component opened by OpenSettings call. A component
+ * can be an item in a section, e.g., scroll direction item in touchpad
+ * section.
+ */
+enum SettingsComponent {
+  BLUETOOTH,
+  // TODO(b/365595552): add remaining required components.
+};
+
 // Browser interface for chrome://help-app to bootstrap a connection.
 interface PageHandlerFactory {
   // Create a page handler which exposes interfaces implemented in the browser
@@ -87,4 +97,9 @@
   // Failure to provide a valid https:// URL will cause the Help app renderer
   // process to crash.
   OpenUrlInBrowserAndTriggerInstallDialog(url.mojom.Url url);
+
+  // Opens an os settings for the specified settings component. If a specified
+  // component is not available on a device, e.g., a trackpad settings specified
+  // while a device does not have it, it is handled as no-op.
+  OpenSettings(SettingsComponent component);
 };
diff --git a/ash/webui/help_app_ui/help_app_ui_delegate.h b/ash/webui/help_app_ui/help_app_ui_delegate.h
index abe38ee0..cbf6df8 100644
--- a/ash/webui/help_app_ui/help_app_ui_delegate.h
+++ b/ash/webui/help_app_ui/help_app_ui_delegate.h
@@ -59,6 +59,11 @@
   // process to crash.
   virtual std::optional<std::string> OpenUrlInBrowserAndTriggerInstallDialog(
       const GURL& url) = 0;
+
+  // Open an os settings of a specified settings component. If a specified
+  // component is not available on a device, it is handled as no-op.
+  virtual void OpenSettings(
+      ash::help_app::mojom::SettingsComponent component) = 0;
 };
 
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
index d50c783..59466fa 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -534,7 +534,8 @@
 // Create a string which consists of the bounds and the state for comparison.
 std::string GetPlacementString(const gfx::Rect& bounds,
                                ui::WindowShowState state) {
-  return bounds.ToString() + ' ' + base::NumberToString(state);
+  return bounds.ToString() + ' ' +
+         base::NumberToString(static_cast<int>(state));
 }
 
 // Retrieves the window's restore state override - if any - and returns it as a
diff --git a/base/android/binder_unittest.cc b/base/android/binder_unittest.cc
index f9f0096..3ad763bd 100644
--- a/base/android/binder_unittest.cc
+++ b/base/android/binder_unittest.cc
@@ -606,7 +606,8 @@
   EXPECT_TRUE(JoinChild(child));
 }
 
-TEST_F(BinderMultiprocessTest, AssociateInvalid) {
+// (crbug.com/365998251): Builder failing this unittest.
+TEST_F(BinderMultiprocessTest, DISABLED_AssociateInvalid) {
   auto sink = base::MakeRefCounted<BinderSink>(*this);
   Process child = sink->LaunchChildAndWaitForBinder(
       "Associate_Child", [](BinderRef binder) {
diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java
index e16221fe..c448392 100644
--- a/base/android/java/src/org/chromium/base/ContentUriUtils.java
+++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java
@@ -277,6 +277,20 @@
         assert isContentUri(uriString);
         Uri parsedUri = Uri.parse(uriString);
         ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
-        return resolver.delete(parsedUri, null, null) > 0;
+        try {
+            if (DocumentsContract.deleteDocument(resolver, parsedUri)) {
+                return true;
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "DocumentsContract could not delete %s: %s", uriString, e.getMessage());
+        }
+        try {
+            if (resolver.delete(parsedUri, null, null) > 0) {
+                return true;
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "ContentResolver could not delete %s: %s", uriString, e.getMessage());
+        }
+        return false;
     }
 }
diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc
index 304c07f..0367ed7 100644
--- a/base/message_loop/message_pump_win.cc
+++ b/base/message_loop/message_pump_win.cc
@@ -14,8 +14,6 @@
 #include "base/auto_reset.h"
 #include "base/check.h"
 #include "base/debug/alias.h"
-#include "base/debug/dump_without_crashing.h"
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
@@ -389,26 +387,6 @@
 void MessagePumpForUI::HandleWorkMessage() {
   DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
 
-  if (!g_ui_pump_improvements_win && in_dispatch_message_ &&
-      !in_nested_native_loop_with_application_tasks_) {
-    // An undeclared nested native loop is spinning in DispatchMessage(), which
-    // will pump application tasks (task execution is not disabled since
-    // DoWork() is not on the stack). Since UIPumpImprovementsWin will break
-    // this use case (application tasks will no longer run because kMsgHaveWork
-    // is not pumped), investigation is being done into stacks where this occurs
-    // and whether they need to be remediated.
-    //
-    // A `StackTrace` is used to deduplicate stacks to ensure that all instances
-    // of undeclared nested runloops are detected during a session.
-    //
-    // TODO(crbug.com/335672561): Remove this once data has been analyzed.
-    uintptr_t id = 0;
-    for (const void* address : debug::StackTrace().addresses()) {
-      id ^= reinterpret_cast<uintptr_t>(address);
-    }
-    debug::DumpWithoutCrashingWithUniqueId(id);
-  }
-
   // If we are being called outside of the context of Run, then don't try to do
   // any work.  This could correspond to a MessageBox call or something of that
   // sort.
@@ -633,10 +611,7 @@
   for (Observer& observer : observers_)
     observer.WillDispatchMSG(msg);
   ::TranslateMessage(&msg);
-  {
-    AutoReset<bool> reset(&in_dispatch_message_, true);
-    ::DispatchMessage(&msg);
-  }
+  ::DispatchMessage(&msg);
   for (Observer& observer : observers_)
     observer.DidDispatchMSG(msg);
 
diff --git a/base/message_loop/message_pump_win.h b/base/message_loop/message_pump_win.h
index bfe4bea..b5d1faf 100644
--- a/base/message_loop/message_pump_win.h
+++ b/base/message_loop/message_pump_win.h
@@ -197,13 +197,6 @@
   //   - HandleNestedNativeLoopWithApplicationTasks(false) is called.
   bool in_nested_native_loop_with_application_tasks_ = false;
 
-  // This is set upon entering DispatchMessage() in ProcessNextWindowsMessage(),
-  // and unset upon exit. It is used to track nested runloops which pump during
-  // DispatchMessage(), a use case currently broken by UIPumpImprovementsWin
-  // (since, when they are undeclared, do not pump kMsgHaveWork, but do without
-  // the feature enabled).
-  bool in_dispatch_message_ = false;
-
   enum class WakeupState {
     kApplicationTask,
     kNative,
diff --git a/base/nix/xdg_util.cc b/base/nix/xdg_util.cc
index becdd86..b6f7026 100644
--- a/base/nix/xdg_util.cc
+++ b/base/nix/xdg_util.cc
@@ -311,4 +311,24 @@
       base::BindOnce(create_token_cb, std::move(callback)));
 }
 
+std::string XdgDesktopPortalRequestPath(const std::string& sender,
+                                        const std::string& token) {
+  // Since version 0.9 of xdg-desktop-portal, the handle will be of the form
+  // /org/freedesktop/portal/desktop/request/SENDER/TOKEN where SENDER is the
+  // callers unique name, with the initial ':' removed and all '.' replaced by
+  // '_', and TOKEN is a unique token that the caller provided with the
+  // handle_token key in the options vardict. See:
+  // https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html
+  static constexpr char kObjectPathRequestFormat[] =
+      "/org/freedesktop/portal/desktop/request/$1/$2";
+
+  auto sender_name =
+      !sender.empty() && sender[0] == ':' ? sender.substr(1) : sender;
+  std::string bus_name;
+  base::ReplaceChars(sender_name, ".", "_", &bus_name);
+  return ReplaceStringPlaceholders(kObjectPathRequestFormat,
+                                   std::vector<std::string>{bus_name, token},
+                                   nullptr);
+}
+
 }  // namespace base::nix
diff --git a/base/nix/xdg_util.h b/base/nix/xdg_util.h
index 8784be3..9bbff17 100644
--- a/base/nix/xdg_util.h
+++ b/base/nix/xdg_util.h
@@ -87,7 +87,8 @@
 // a directory path. |fallback_dir| is the directory relative to $HOME that we
 // use if |env_name| cannot be found or is empty. |fallback_dir| may be NULL.
 // Examples of |env_name| are XDG_CONFIG_HOME and XDG_DATA_HOME.
-BASE_EXPORT FilePath GetXDGDirectory(Environment* env, const char* env_name,
+BASE_EXPORT FilePath GetXDGDirectory(Environment* env,
+                                     const char* env_name,
                                      const char* fallback_dir);
 
 // Wrapper around xdg_user_dir_lookup() from src/base/third_party/xdg-user-dirs
@@ -146,6 +147,12 @@
 BASE_EXPORT void CreateLaunchOptionsWithXdgActivation(
     XdgActivationLaunchOptionsCallback callback);
 
+// Returns a request path as specified in v0.9 of xdg-desktop-portal:
+// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html
+BASE_EXPORT
+std::string XdgDesktopPortalRequestPath(const std::string& sender,
+                                        const std::string& token);
+
 }  // namespace nix
 }  // namespace base
 
diff --git a/base/threading/thread_restrictions.cc b/base/threading/thread_restrictions.cc
index 55633f7..9ec4c77e 100644
--- a/base/threading/thread_restrictions.cc
+++ b/base/threading/thread_restrictions.cc
@@ -4,58 +4,54 @@
 
 #include "base/threading/thread_restrictions.h"
 
+#include "base/check.h"
 #include "base/threading/hang_watcher.h"
 #include "base/trace_event/base_tracing.h"
 #include "build/build_config.h"
-
-#if DCHECK_IS_ON()
-#include "base/check_op.h"
 #include "third_party/abseil-cpp/absl/base/attributes.h"
 
-// NaCL doesn't support stack sampling and Android is slow at stack sampling and
-// this causes timeouts (crbug.com/959139).
-#if BUILDFLAG(IS_NACL) || BUILDFLAG(IS_ANDROID)
-constexpr bool kCaptureStackTraces = false;
-#else
-// Always disabled when !EXPENSIVE_DCHECKS_ARE_ON() because user-facing builds
-// typically drop log strings anyways.
-constexpr bool kCaptureStackTraces = EXPENSIVE_DCHECKS_ARE_ON();
-#endif
-
 namespace base {
 
-BooleanWithStack::BooleanWithStack(bool value) : value_(value) {
-  if (kCaptureStackTraces) {
-    stack_.emplace();
-  }
+BooleanWithOptionalStack::BooleanWithOptionalStack(bool value) : value_(value) {
+#if CAPTURE_THREAD_RESTRICTIONS_STACK_TRACES()
+  stack_.emplace();
+#endif  // CAPTURE_THREAD_RESTRICTIONS_STACK_TRACES()
 }
 
-std::ostream& operator<<(std::ostream& out, const BooleanWithStack& bws) {
+std::ostream& operator<<(std::ostream& out,
+                         const BooleanWithOptionalStack& bws) {
   out << bws.value_;
-  if (kCaptureStackTraces) {
-    if (bws.stack_.has_value()) {
-      out << " set by\n" << bws.stack_.value();
-    } else {
-      out << " (value by default)";
-    }
+#if CAPTURE_THREAD_RESTRICTIONS_STACK_TRACES()
+  if (bws.stack_.has_value()) {
+    out << " set by\n" << bws.stack_.value();
+  } else {
+    out << " (value by default)";
   }
+#endif  // CAPTURE_THREAD_RESTRICTIONS_STACK_TRACES()
   return out;
 }
 
+// A macro that dumps in official builds (non-fatal) if the condition is false,
+// or behaves as DCHECK in DCHECK-enabled builds. Unlike DUMP_WILL_BE_CHECK,
+// there is no intent to transform those into CHECKs. Used to report potential
+// performance issues.
+#define DUMP_OR_DCHECK DUMP_WILL_BE_CHECK
+
 namespace {
 
-ABSL_CONST_INIT thread_local BooleanWithStack tls_blocking_disallowed;
-ABSL_CONST_INIT thread_local BooleanWithStack tls_singleton_disallowed;
-ABSL_CONST_INIT thread_local BooleanWithStack
+ABSL_CONST_INIT thread_local BooleanWithOptionalStack tls_blocking_disallowed;
+ABSL_CONST_INIT thread_local BooleanWithOptionalStack tls_singleton_disallowed;
+ABSL_CONST_INIT thread_local BooleanWithOptionalStack
     tls_base_sync_primitives_disallowed;
-ABSL_CONST_INIT thread_local BooleanWithStack tls_cpu_intensive_work_disallowed;
+ABSL_CONST_INIT thread_local BooleanWithOptionalStack
+    tls_cpu_intensive_work_disallowed;
 
 }  // namespace
 
 namespace internal {
 
 void AssertBlockingAllowed() {
-  DCHECK(!tls_blocking_disallowed)
+  DUMP_OR_DCHECK(!tls_blocking_disallowed)
       << "Function marked as blocking was called from a scope that disallows "
          "blocking! If this task is running inside the ThreadPool, it needs "
          "to have MayBlock() in its TaskTraits. Otherwise, consider making "
@@ -72,11 +68,11 @@
 }  // namespace internal
 
 void DisallowBlocking() {
-  tls_blocking_disallowed = BooleanWithStack(true);
+  tls_blocking_disallowed = BooleanWithOptionalStack(true);
 }
 
 ScopedDisallowBlocking::ScopedDisallowBlocking()
-    : resetter_(&tls_blocking_disallowed, BooleanWithStack(true)) {}
+    : resetter_(&tls_blocking_disallowed, BooleanWithOptionalStack(true)) {}
 
 ScopedDisallowBlocking::~ScopedDisallowBlocking() {
   DCHECK(tls_blocking_disallowed)
@@ -86,11 +82,12 @@
 }
 
 void DisallowBaseSyncPrimitives() {
-  tls_base_sync_primitives_disallowed = BooleanWithStack(true);
+  tls_base_sync_primitives_disallowed = BooleanWithOptionalStack(true);
 }
 
 ScopedDisallowBaseSyncPrimitives::ScopedDisallowBaseSyncPrimitives()
-    : resetter_(&tls_base_sync_primitives_disallowed, BooleanWithStack(true)) {}
+    : resetter_(&tls_base_sync_primitives_disallowed,
+                BooleanWithOptionalStack(true)) {}
 
 ScopedDisallowBaseSyncPrimitives::~ScopedDisallowBaseSyncPrimitives() {
   DCHECK(tls_base_sync_primitives_disallowed)
@@ -101,7 +98,8 @@
 }
 
 ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives()
-    : resetter_(&tls_base_sync_primitives_disallowed, BooleanWithStack(false)) {
+    : resetter_(&tls_base_sync_primitives_disallowed,
+                BooleanWithOptionalStack(false)) {
   DCHECK(!tls_blocking_disallowed)
       << "To allow //base sync primitives in a scope where blocking is "
          "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.\n"
@@ -118,8 +116,8 @@
 
 ScopedAllowBaseSyncPrimitivesForTesting::
     ScopedAllowBaseSyncPrimitivesForTesting()
-    : resetter_(&tls_base_sync_primitives_disallowed, BooleanWithStack(false)) {
-}
+    : resetter_(&tls_base_sync_primitives_disallowed,
+                BooleanWithOptionalStack(false)) {}
 
 ScopedAllowBaseSyncPrimitivesForTesting::
     ~ScopedAllowBaseSyncPrimitivesForTesting() {
@@ -132,10 +130,11 @@
 
 ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting()
     : base_sync_resetter_(&tls_base_sync_primitives_disallowed,
-                          BooleanWithStack(false)),
-      blocking_resetter_(&tls_blocking_disallowed, BooleanWithStack(false)),
+                          BooleanWithOptionalStack(false)),
+      blocking_resetter_(&tls_blocking_disallowed,
+                         BooleanWithOptionalStack(false)),
       cpu_resetter_(&tls_cpu_intensive_work_disallowed,
-                    BooleanWithStack(false)) {}
+                    BooleanWithOptionalStack(false)) {}
 
 ScopedAllowUnresponsiveTasksForTesting::
     ~ScopedAllowUnresponsiveTasksForTesting() {
@@ -158,7 +157,7 @@
 namespace internal {
 
 void AssertBaseSyncPrimitivesAllowed() {
-  DCHECK(!tls_base_sync_primitives_disallowed)
+  DUMP_OR_DCHECK(!tls_base_sync_primitives_disallowed)
       << "Waiting on a //base sync primitive is not allowed on this thread to "
          "prevent jank and deadlock. If waiting on a //base sync primitive is "
          "unavoidable, do it within the scope of a "
@@ -171,14 +170,14 @@
 }
 
 void ResetThreadRestrictionsForTesting() {
-  tls_blocking_disallowed = BooleanWithStack(false);
-  tls_singleton_disallowed = BooleanWithStack(false);
-  tls_base_sync_primitives_disallowed = BooleanWithStack(false);
-  tls_cpu_intensive_work_disallowed = BooleanWithStack(false);
+  tls_blocking_disallowed = BooleanWithOptionalStack(false);
+  tls_singleton_disallowed = BooleanWithOptionalStack(false);
+  tls_base_sync_primitives_disallowed = BooleanWithOptionalStack(false);
+  tls_cpu_intensive_work_disallowed = BooleanWithOptionalStack(false);
 }
 
 void AssertSingletonAllowed() {
-  DCHECK(!tls_singleton_disallowed)
+  DUMP_OR_DCHECK(!tls_singleton_disallowed)
       << "LazyInstance/Singleton is not allowed to be used on this thread. "
          "Most likely it's because this thread is not joinable (or the current "
          "task is running with TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN "
@@ -192,11 +191,11 @@
 }  // namespace internal
 
 void DisallowSingleton() {
-  tls_singleton_disallowed = BooleanWithStack(true);
+  tls_singleton_disallowed = BooleanWithOptionalStack(true);
 }
 
 ScopedDisallowSingleton::ScopedDisallowSingleton()
-    : resetter_(&tls_singleton_disallowed, BooleanWithStack(true)) {}
+    : resetter_(&tls_singleton_disallowed, BooleanWithOptionalStack(true)) {}
 
 ScopedDisallowSingleton::~ScopedDisallowSingleton() {
   DCHECK(tls_singleton_disallowed)
@@ -206,7 +205,7 @@
 }
 
 void AssertLongCPUWorkAllowed() {
-  DCHECK(!tls_cpu_intensive_work_disallowed)
+  DUMP_OR_DCHECK(!tls_cpu_intensive_work_disallowed)
       << "Function marked as CPU intensive was called from a scope that "
          "disallows this kind of work! Consider making this work "
          "asynchronous.\n"
@@ -217,30 +216,21 @@
 void DisallowUnresponsiveTasks() {
   DisallowBlocking();
   DisallowBaseSyncPrimitives();
-  tls_cpu_intensive_work_disallowed = BooleanWithStack(true);
+  tls_cpu_intensive_work_disallowed = BooleanWithOptionalStack(true);
 }
 
 // static
 void PermanentThreadAllowance::AllowBlocking() {
-  tls_blocking_disallowed = BooleanWithStack(false);
+  tls_blocking_disallowed = BooleanWithOptionalStack(false);
 }
 
 // static
 void PermanentThreadAllowance::AllowBaseSyncPrimitives() {
-  tls_base_sync_primitives_disallowed = BooleanWithStack(false);
+  tls_base_sync_primitives_disallowed = BooleanWithOptionalStack(false);
 }
 
-}  // namespace base
-
-#endif  // DCHECK_IS_ON()
-
-namespace base {
-
 ScopedAllowBlocking::ScopedAllowBlocking(const Location& from_here)
-#if DCHECK_IS_ON()
-    : resetter_(&tls_blocking_disallowed, BooleanWithStack(false))
-#endif
-{
+    : resetter_(&tls_blocking_disallowed, BooleanWithOptionalStack(false)) {
   TRACE_EVENT_BEGIN(
       "base", "ScopedAllowBlocking", [&](perfetto::EventContext ctx) {
         ctx.event()->set_source_location_iid(
@@ -251,20 +241,16 @@
 ScopedAllowBlocking::~ScopedAllowBlocking() {
   TRACE_EVENT_END0("base", "ScopedAllowBlocking");
 
-#if DCHECK_IS_ON()
   DCHECK(!tls_blocking_disallowed)
       << "~ScopedAllowBlocking() running while surprisingly already no longer "
          "allowed.\n"
       << "tls_blocking_disallowed " << tls_blocking_disallowed;
-#endif
 }
 
 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
     ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(const Location& from_here)
-#if DCHECK_IS_ON()
-    : resetter_(&tls_base_sync_primitives_disallowed, BooleanWithStack(false))
-#endif
-{
+    : resetter_(&tls_base_sync_primitives_disallowed,
+                BooleanWithOptionalStack(false)) {
   TRACE_EVENT_BEGIN(
       "base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope",
       [&](perfetto::EventContext ctx) {
@@ -282,13 +268,11 @@
     ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
   TRACE_EVENT_END0("base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope");
 
-#if DCHECK_IS_ON()
   DCHECK(!tls_base_sync_primitives_disallowed)
       << "~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() running while "
          "surprisingly already no longer allowed.\n"
       << "tls_base_sync_primitives_disallowed "
       << tls_base_sync_primitives_disallowed;
-#endif
 }
 
 }  // namespace base
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index b40103d..0de909d 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -5,20 +5,16 @@
 #ifndef BASE_THREADING_THREAD_RESTRICTIONS_H_
 #define BASE_THREADING_THREAD_RESTRICTIONS_H_
 
+#include <optional>
+
 #include "base/auto_reset.h"
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
-#include "base/dcheck_is_on.h"
+#include "base/debug/stack_trace.h"
 #include "base/gtest_prod_util.h"
 #include "base/location.h"
 #include "build/build_config.h"
 
-#if DCHECK_IS_ON()
-#include <optional>
-
-#include "base/debug/stack_trace.h"
-#endif
-
 // -----------------------------------------------------------------------------
 // Usage documentation
 // -----------------------------------------------------------------------------
@@ -505,76 +501,66 @@
 class TestCustomDisallow;
 class Thread;
 
-#if DCHECK_IS_ON()
-// NOT_TAIL_CALLED if dcheck-is-on so it's always evident who irrevocably
-// altered the allowance (dcheck-builds will provide the setter's stack on
-// assertion) or who made a failing Assert*() call.
-#define INLINE_OR_NOT_TAIL_CALLED NOT_TAIL_CALLED BASE_EXPORT
-#define EMPTY_BODY_IF_DCHECK_IS_OFF
-#define DEFAULT_IF_DCHECK_IS_OFF
+// NaCL doesn't support stack capture.
+// Android can hang in stack capture (crbug.com/959139).
+#if BUILDFLAG(IS_NACL) || BUILDFLAG(IS_ANDROID)
+#define CAPTURE_THREAD_RESTRICTIONS_STACK_TRACES() false
+#else
+// Stack capture is slow. Only enable it in developer builds, to avoid user
+// visible jank when thread restrictions are set.
+#define CAPTURE_THREAD_RESTRICTIONS_STACK_TRACES() EXPENSIVE_DCHECKS_ARE_ON()
+#endif
 
-class BooleanWithStack {
+// A boolean and the stack from which it was set. Note: The stack is not
+// captured in all builds, see `CAPTURE_THREAD_RESTRICTIONS_STACK_TRACES()`.
+class BooleanWithOptionalStack {
  public:
   // Default value.
-  BooleanWithStack() = default;
+  BooleanWithOptionalStack() = default;
 
   // Value when explicitly set.
-  explicit BooleanWithStack(bool value);
+  explicit BooleanWithOptionalStack(bool value);
 
   explicit operator bool() const { return value_; }
 
   friend std::ostream& operator<<(std::ostream& out,
-                                  const BooleanWithStack& bws);
+                                  const BooleanWithOptionalStack& bws);
 
  private:
   bool value_ = false;
+#if CAPTURE_THREAD_RESTRICTIONS_STACK_TRACES()
   std::optional<debug::StackTrace> stack_;
+#endif
 };
 
-#else
-// inline if dcheck-is-off so it's no overhead
-#define INLINE_OR_NOT_TAIL_CALLED inline
-
-// The static_assert() eats follow-on semicolons.
-#define EMPTY_BODY_IF_DCHECK_IS_OFF \
-  {}                                \
-  static_assert(true)
-
-#define DEFAULT_IF_DCHECK_IS_OFF = default
-#endif  // DCHECK_IS_ON()
-
 namespace internal {
 
 // Asserts that blocking calls are allowed in the current scope. This is an
 // internal call, external code should use ScopedBlockingCall instead, which
 // serves as a precise annotation of the scope that may/will block.
-INLINE_OR_NOT_TAIL_CALLED void AssertBlockingAllowed()
-    EMPTY_BODY_IF_DCHECK_IS_OFF;
-INLINE_OR_NOT_TAIL_CALLED void AssertBlockingDisallowedForTesting()
-    EMPTY_BODY_IF_DCHECK_IS_OFF;
+NOT_TAIL_CALLED BASE_EXPORT void AssertBlockingAllowed();
+NOT_TAIL_CALLED BASE_EXPORT void AssertBlockingDisallowedForTesting();
 
 }  // namespace internal
 
 // Disallows blocking on the current thread.
-INLINE_OR_NOT_TAIL_CALLED void DisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+NOT_TAIL_CALLED BASE_EXPORT void DisallowBlocking();
 
 // Disallows blocking calls within its scope.
-class BASE_EXPORT [[maybe_unused, nodiscard]] ScopedDisallowBlocking {
+class BASE_EXPORT ScopedDisallowBlocking {
  public:
-  ScopedDisallowBlocking() DEFAULT_IF_DCHECK_IS_OFF;
+  ScopedDisallowBlocking();
 
   ScopedDisallowBlocking(const ScopedDisallowBlocking&) = delete;
   ScopedDisallowBlocking& operator=(const ScopedDisallowBlocking&) = delete;
 
-  ~ScopedDisallowBlocking() DEFAULT_IF_DCHECK_IS_OFF;
+  ~ScopedDisallowBlocking();
 
  private:
-#if DCHECK_IS_ON()
-  const AutoReset<BooleanWithStack> resetter_;
-#endif
+  const AutoReset<BooleanWithOptionalStack> resetter_;
 };
 
-class BASE_EXPORT [[maybe_unused, nodiscard]] ScopedAllowBlocking {
+class BASE_EXPORT ScopedAllowBlocking {
  public:
   ScopedAllowBlocking(const ScopedAllowBlocking&) = delete;
   ScopedAllowBlocking& operator=(const ScopedAllowBlocking&) = delete;
@@ -705,12 +691,10 @@
   ScopedAllowBlocking(const Location& from_here = Location::Current());
   ~ScopedAllowBlocking();
 
-#if DCHECK_IS_ON()
-  const AutoReset<BooleanWithStack> resetter_;
-#endif
+  const AutoReset<BooleanWithOptionalStack> resetter_;
 };
 
-class [[maybe_unused, nodiscard]] ScopedAllowBlockingForTesting {
+class ScopedAllowBlockingForTesting {
  public:
   ScopedAllowBlockingForTesting() = default;
 
@@ -721,33 +705,28 @@
   ~ScopedAllowBlockingForTesting() = default;
 
  private:
-#if DCHECK_IS_ON()
   ScopedAllowBlocking scoped_allow_blocking_;
-#endif
 };
 
-INLINE_OR_NOT_TAIL_CALLED void DisallowBaseSyncPrimitives()
-    EMPTY_BODY_IF_DCHECK_IS_OFF;
+NOT_TAIL_CALLED BASE_EXPORT void DisallowBaseSyncPrimitives();
 
 // Disallows singletons within its scope.
-class BASE_EXPORT [[maybe_unused, nodiscard]] ScopedDisallowBaseSyncPrimitives {
+class BASE_EXPORT ScopedDisallowBaseSyncPrimitives {
  public:
-  ScopedDisallowBaseSyncPrimitives() DEFAULT_IF_DCHECK_IS_OFF;
+  ScopedDisallowBaseSyncPrimitives();
 
   ScopedDisallowBaseSyncPrimitives(const ScopedDisallowBaseSyncPrimitives&) =
       delete;
   ScopedDisallowBaseSyncPrimitives& operator=(
       const ScopedDisallowBaseSyncPrimitives&) = delete;
 
-  ~ScopedDisallowBaseSyncPrimitives() DEFAULT_IF_DCHECK_IS_OFF;
+  ~ScopedDisallowBaseSyncPrimitives();
 
  private:
-#if DCHECK_IS_ON()
-  const AutoReset<BooleanWithStack> resetter_;
-#endif
+  const AutoReset<BooleanWithOptionalStack> resetter_;
 };
 
-class BASE_EXPORT [[maybe_unused, nodiscard]] ScopedAllowBaseSyncPrimitives {
+class BASE_EXPORT ScopedAllowBaseSyncPrimitives {
  public:
   ScopedAllowBaseSyncPrimitives(const ScopedAllowBaseSyncPrimitives&) = delete;
   ScopedAllowBaseSyncPrimitives& operator=(
@@ -825,12 +804,10 @@
   friend class viz::SkiaOutputSurfaceImpl;       // http://crbug.com/341151462
   friend class viz::SharedImageInterfaceProvider;  // http://crbug.com/341151462
 
-  ScopedAllowBaseSyncPrimitives() DEFAULT_IF_DCHECK_IS_OFF;
-  ~ScopedAllowBaseSyncPrimitives() DEFAULT_IF_DCHECK_IS_OFF;
+  ScopedAllowBaseSyncPrimitives();
+  ~ScopedAllowBaseSyncPrimitives();
 
-#if DCHECK_IS_ON()
-  const AutoReset<BooleanWithStack> resetter_;
-#endif
+  const AutoReset<BooleanWithOptionalStack> resetter_;
 };
 
 class BASE_EXPORT
@@ -938,9 +915,7 @@
 
   ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope();
 
-#if DCHECK_IS_ON()
-  const AutoReset<BooleanWithStack> resetter_;
-#endif
+  const AutoReset<BooleanWithOptionalStack> resetter_;
 };
 
 // Allow base-sync-primitives in tests, doesn't require explicit friend'ing like
@@ -948,89 +923,76 @@
 // Note: For WaitableEvents in the test logic, base::TestWaitableEvent is
 // exposed as a convenience to avoid the need for
 // ScopedAllowBaseSyncPrimitivesForTesting.
-class BASE_EXPORT
-    [[maybe_unused, nodiscard]] ScopedAllowBaseSyncPrimitivesForTesting {
+class BASE_EXPORT ScopedAllowBaseSyncPrimitivesForTesting {
  public:
-  ScopedAllowBaseSyncPrimitivesForTesting() DEFAULT_IF_DCHECK_IS_OFF;
+  ScopedAllowBaseSyncPrimitivesForTesting();
 
   ScopedAllowBaseSyncPrimitivesForTesting(
       const ScopedAllowBaseSyncPrimitivesForTesting&) = delete;
   ScopedAllowBaseSyncPrimitivesForTesting& operator=(
       const ScopedAllowBaseSyncPrimitivesForTesting&) = delete;
 
-  ~ScopedAllowBaseSyncPrimitivesForTesting() DEFAULT_IF_DCHECK_IS_OFF;
+  ~ScopedAllowBaseSyncPrimitivesForTesting();
 
  private:
-#if DCHECK_IS_ON()
-  const AutoReset<BooleanWithStack> resetter_;
-#endif
+  const AutoReset<BooleanWithOptionalStack> resetter_;
 };
 
 // Counterpart to base::DisallowUnresponsiveTasks() for tests to allow them to
 // block their thread after it was banned.
-class BASE_EXPORT
-    [[maybe_unused, nodiscard]] ScopedAllowUnresponsiveTasksForTesting {
+class BASE_EXPORT ScopedAllowUnresponsiveTasksForTesting {
  public:
-  ScopedAllowUnresponsiveTasksForTesting() DEFAULT_IF_DCHECK_IS_OFF;
+  ScopedAllowUnresponsiveTasksForTesting();
 
   ScopedAllowUnresponsiveTasksForTesting(
       const ScopedAllowUnresponsiveTasksForTesting&) = delete;
   ScopedAllowUnresponsiveTasksForTesting& operator=(
       const ScopedAllowUnresponsiveTasksForTesting&) = delete;
 
-  ~ScopedAllowUnresponsiveTasksForTesting() DEFAULT_IF_DCHECK_IS_OFF;
+  ~ScopedAllowUnresponsiveTasksForTesting();
 
  private:
-#if DCHECK_IS_ON()
-  const AutoReset<BooleanWithStack> base_sync_resetter_;
-  const AutoReset<BooleanWithStack> blocking_resetter_;
-  const AutoReset<BooleanWithStack> cpu_resetter_;
-#endif
+  const AutoReset<BooleanWithOptionalStack> base_sync_resetter_;
+  const AutoReset<BooleanWithOptionalStack> blocking_resetter_;
+  const AutoReset<BooleanWithOptionalStack> cpu_resetter_;
 };
 
 namespace internal {
 
 // Asserts that waiting on a //base sync primitive is allowed in the current
 // scope.
-INLINE_OR_NOT_TAIL_CALLED void AssertBaseSyncPrimitivesAllowed()
-    EMPTY_BODY_IF_DCHECK_IS_OFF;
+NOT_TAIL_CALLED BASE_EXPORT void AssertBaseSyncPrimitivesAllowed();
 
 // Resets all thread restrictions on the current thread.
-INLINE_OR_NOT_TAIL_CALLED void ResetThreadRestrictionsForTesting()
-    EMPTY_BODY_IF_DCHECK_IS_OFF;
+BASE_EXPORT void ResetThreadRestrictionsForTesting();
 
 // Check whether the current thread is allowed to use singletons (Singleton /
 // LazyInstance).  DCHECKs if not.
-INLINE_OR_NOT_TAIL_CALLED void AssertSingletonAllowed()
-    EMPTY_BODY_IF_DCHECK_IS_OFF;
+NOT_TAIL_CALLED BASE_EXPORT void AssertSingletonAllowed();
 
 }  // namespace internal
 
 // Disallow using singleton on the current thread.
-INLINE_OR_NOT_TAIL_CALLED void DisallowSingleton() EMPTY_BODY_IF_DCHECK_IS_OFF;
+NOT_TAIL_CALLED BASE_EXPORT void DisallowSingleton();
 
 // Disallows singletons within its scope.
-class BASE_EXPORT [[maybe_unused, nodiscard]] ScopedDisallowSingleton {
+class BASE_EXPORT ScopedDisallowSingleton {
  public:
-  ScopedDisallowSingleton() DEFAULT_IF_DCHECK_IS_OFF;
+  ScopedDisallowSingleton();
 
   ScopedDisallowSingleton(const ScopedDisallowSingleton&) = delete;
   ScopedDisallowSingleton& operator=(const ScopedDisallowSingleton&) = delete;
 
-  ~ScopedDisallowSingleton() DEFAULT_IF_DCHECK_IS_OFF;
+  ~ScopedDisallowSingleton();
 
  private:
-#if DCHECK_IS_ON()
-  const AutoReset<BooleanWithStack> resetter_;
-#endif
+  const AutoReset<BooleanWithOptionalStack> resetter_;
 };
 
 // Asserts that running long CPU work is allowed in the current scope.
-INLINE_OR_NOT_TAIL_CALLED void AssertLongCPUWorkAllowed()
-    EMPTY_BODY_IF_DCHECK_IS_OFF;
+NOT_TAIL_CALLED BASE_EXPORT void AssertLongCPUWorkAllowed();
 
-INLINE_OR_NOT_TAIL_CALLED void DisallowUnresponsiveTasks()
-    EMPTY_BODY_IF_DCHECK_IS_OFF;
+NOT_TAIL_CALLED BASE_EXPORT void DisallowUnresponsiveTasks();
 
 // Friend-only methods to permanently allow the current thread to use
 // blocking/sync-primitives calls. Threads start out in the *allowed* state but
@@ -1054,14 +1016,10 @@
 #endif  // BUILDFLAG(IS_IOS)
   friend class web::WebMainLoop;
 
-  static void AllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
-  static void AllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
+  static void AllowBlocking();
+  static void AllowBaseSyncPrimitives();
 };
 
-#undef INLINE_OR_NOT_TAIL_CALLED
-#undef EMPTY_BODY_IF_DCHECK_IS_OFF
-#undef DEFAULT_IF_DCHECK_IS_OFF
-
 }  // namespace base
 
 #endif  // BASE_THREADING_THREAD_RESTRICTIONS_H_
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index 2f1c81a..ccd31464 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -167,8 +167,8 @@
   additional_space += 56 * 1024;
 #endif
 #if DCHECK_IS_ON()
-  // The thread restrictions add four BooleanWithStacks (which are ~2k each).
-  additional_space += sizeof(BooleanWithStack) * 4;
+  // The thread restrictions add four BooleanWithOptionalStacks (~2k each).
+  additional_space += sizeof(BooleanWithOptionalStack) * 4;
 #endif
 
   Thread a("StartWithStackSize");
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto
index ceddb270..abaf7fa 100644
--- a/base/tracing/protos/chrome_track_event.proto
+++ b/base/tracing/protos/chrome_track_event.proto
@@ -2062,9 +2062,60 @@
   optional InputType input_type = 8;
 }
 
+message EventTiming {
+  optional bool cancelable = 1;
+  optional string frame = 2;
+  optional uint32 interaction_id = 3;
+  optional uint32 interaction_offset = 4;
+  optional int64 node_id = 5;
+  optional int64 key_code = 6;
+  optional int32 pointer_id = 7;
+  optional uint64 fallback_time_us = 8;
+  enum EventType {
+    UNDEFINED = 0;
+    AUX_CLICK_EVENT = 1;
+    CLICK_EVENT = 2;
+    CONTEXT_MENU_EVENT = 3;
+    DOUBLE_CLICK_EVENT = 4;
+    MOUSE_DOWN_EVENT = 5;
+    MOUSE_ENTER_EVENT = 6;
+    MOUSE_LEAVE_EVENT = 7;
+    MOUSE_OUT_EVENT = 9;
+    MOUSE_OVER_EVENT = 10;
+    MOUSE_UP_EVENT = 11;
+    POINTER_OVER_EVENT = 12;
+    POINTER_ENTER_EVENT = 13;
+    POINTER_DOWN_EVENT = 14;
+    POINTER_UP_EVENT = 15;
+    POINTER_CANCEL_EVENT = 16;
+    POINTER_OUT_EVENT = 17;
+    POINTER_LEAVE_EVENT = 18;
+    GOT_POINTER_CAPTURE_EVENT = 19;
+    LOST_POINTER_CAPTURE_EVENT = 20;
+    TOUCH_START_EVENT = 21;
+    TOUCH_END_EVENT = 22;
+    TOUCH_CANCEL_EVENT = 23;
+    KEY_DOWN_EVENT = 24;
+    KEY_PRESS_EVENT = 25;
+    KEY_UP_EVENT = 26;
+    BEFORE_INPUT_EVENT = 27;
+    INPUT_EVENT = 28;
+    COMPOSITION_START_EVENT = 29;
+    COMPOSITION_UPDATE_EVENT = 30;
+    COMPOSITION_END_EVENT = 31;
+    DRAG_START_EVENT = 32;
+    DRAG_END_EVENT = 33;
+    DRAG_ENTER_EVENT = 34;
+    DRAG_LEAVE_EVENT = 35;
+    DRAG_OVER_EVENT = 36;
+    DROP_EVENT = 37;
+  }
+  optional EventType type = 9;
+}
+
 message ChromeTrackEvent {
   // Extension range for Chrome: 1000-1999
-  // Next ID: 1069
+  // Next ID: 1070
   extend TrackEvent {
     optional ChromeAppState chrome_app_state = 1000;
 
@@ -2210,5 +2261,7 @@
     optional MainFramePipeline main_frame_pipeline = 1067;
 
     optional ChromeLatencyInfo2 chrome_latency_info = 1068;
+
+    optional EventTiming event_timing = 1069;
   }
 }
diff --git a/base/values.cc b/base/values.cc
index 60f57db..e2a47c9 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -259,6 +259,10 @@
   return absl::get_if<BlobStorage>(&data_);
 }
 
+Value::BlobStorage* Value::GetIfBlob() {
+  return absl::get_if<BlobStorage>(&data_);
+}
+
 const Value::Dict* Value::GetIfDict() const {
   return absl::get_if<Dict>(&data_);
 }
@@ -311,6 +315,11 @@
   return absl::get<BlobStorage>(data_);
 }
 
+Value::BlobStorage& Value::GetBlob() {
+  DCHECK(is_blob());
+  return absl::get<BlobStorage>(data_);
+}
+
 const Value::Dict& Value::GetDict() const {
   DCHECK(is_dict());
   return absl::get<Dict>(data_);
@@ -335,6 +344,10 @@
   return std::move(GetString());
 }
 
+Value::BlobStorage Value::TakeBlob() && {
+  return std::move(GetBlob());
+}
+
 Value::Dict Value::TakeDict() && {
   return std::move(GetDict());
 }
@@ -461,6 +474,11 @@
   return v ? v->GetIfBlob() : nullptr;
 }
 
+Value::BlobStorage* Value::Dict::FindBlob(std::string_view key) {
+  Value* v = Find(key);
+  return v ? v->GetIfBlob() : nullptr;
+}
+
 const Value::Dict* Value::Dict::FindDict(std::string_view key) const {
   const Value* v = Find(key);
   return v ? v->GetIfDict() : nullptr;
@@ -681,6 +699,11 @@
   return v ? v->GetIfBlob() : nullptr;
 }
 
+Value::BlobStorage* Value::Dict::FindBlobByDottedPath(std::string_view path) {
+  Value* v = FindByDottedPath(path);
+  return v ? v->GetIfBlob() : nullptr;
+}
+
 const Value::Dict* Value::Dict::FindDictByDottedPath(
     std::string_view path) const {
   const Value* v = FindByDottedPath(path);
diff --git a/base/values.h b/base/values.h
index 6714715..9ad5b07 100644
--- a/base/values.h
+++ b/base/values.h
@@ -257,6 +257,7 @@
   const std::string* GetIfString() const;
   std::string* GetIfString();
   const BlobStorage* GetIfBlob() const;
+  BlobStorage* GetIfBlob();
   const Dict* GetIfDict() const;
   Dict* GetIfDict();
   const List* GetIfList() const;
@@ -273,6 +274,7 @@
   const std::string& GetString() const;
   std::string& GetString();
   const BlobStorage& GetBlob() const;
+  BlobStorage& GetBlob();
   const Dict& GetDict() const;
   Dict& GetDict();
   const List& GetList() const;
@@ -284,6 +286,7 @@
   // Prefer over `std::move(value.Get...())` so clang-tidy can warn about
   // potential use-after-move mistakes.
   std::string TakeString() &&;
+  BlobStorage TakeBlob() &&;
   Dict TakeDict() &&;
   List TakeList() &&;
 
@@ -382,6 +385,7 @@
     const std::string* FindString(std::string_view key) const;
     std::string* FindString(std::string_view key);
     const BlobStorage* FindBlob(std::string_view key) const;
+    BlobStorage* FindBlob(std::string_view key);
     const Dict* FindDict(std::string_view key) const;
     Dict* FindDict(std::string_view key);
     const List* FindList(std::string_view key) const;
@@ -507,6 +511,7 @@
     const std::string* FindStringByDottedPath(std::string_view path) const;
     std::string* FindStringByDottedPath(std::string_view path);
     const BlobStorage* FindBlobByDottedPath(std::string_view path) const;
+    BlobStorage* FindBlobByDottedPath(std::string_view path);
     const Dict* FindDictByDottedPath(std::string_view path) const;
     Dict* FindDictByDottedPath(std::string_view path);
     const List* FindListByDottedPath(std::string_view path) const;
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index f8436d4..a95c8fd 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -972,6 +972,24 @@
   EXPECT_EQ(expected_value, value);
 }
 
+TEST(ValuesTest, MutableFindBlobKey) {
+  Value::BlobStorage original_blob = {0xF, 0x0, 0x0, 0xB, 0xA, 0x2};
+  Value::Dict dict;
+  dict.Set("blob", std::move(original_blob));
+
+  Value::BlobStorage new_blob = {0x0, 0x3, 0x0};
+  *(dict.FindBlob("blob")) = new_blob;
+
+  Value::Dict expected_dict;
+  expected_dict.Set("blob", std::move(new_blob));
+
+  EXPECT_EQ(expected_dict, dict);
+
+  Value value(std::move(dict));
+  Value expected_value(std::move(expected_dict));
+  EXPECT_EQ(expected_value, value);
+}
+
 TEST(ValuesTest, FindDictKey) {
   Value::Dict dict;
   dict.Set("null", Value());
@@ -1648,6 +1666,22 @@
   EXPECT_EQ(value, Value(false));
 }
 
+TEST(ValuesTest, TakeBlob) {
+  Value::BlobStorage original_blob = {0xF, 0x0, 0x0, 0xB, 0xA, 0x2};
+  Value value(original_blob);
+  Value::BlobStorage taken = std::move(value).TakeBlob();
+  EXPECT_EQ(taken, original_blob);
+}
+
+TEST(ValuesTest, PopulateAfterTakeBlob) {
+  Value::BlobStorage original_blob = {0xF, 0x0, 0x0, 0xB, 0xA, 0x2};
+  Value value(original_blob);
+  Value::BlobStorage taken = std::move(value).TakeBlob();
+
+  value = Value(false);
+  EXPECT_EQ(value, Value(false));
+}
+
 TEST(ValuesTest, TakeDict) {
   Value::Dict dict;
   dict.Set("foo", 123);
@@ -2117,6 +2151,29 @@
   EXPECT_EQ("new_value", value.GetString());
 }
 
+TEST(ValuesTest, MutableFindBlobPath) {
+  Value::BlobStorage original_blob = {0xF, 0x0, 0x0, 0xB, 0xA, 0x2};
+  Value::Dict dict;
+  dict.SetByDottedPath("foo.bar", std::move(original_blob));
+
+  Value::BlobStorage new_blob = {0x0, 0x3, 0x0};
+  *(dict.FindBlobByDottedPath("foo.bar")) = new_blob;
+
+  Value::Dict expected_dict;
+  expected_dict.SetByDottedPath("foo.bar", std::move(new_blob));
+
+  EXPECT_EQ(expected_dict, dict);
+}
+
+TEST(ValuesTest, MutableGetBlob) {
+  Value::BlobStorage original_blob = {0xF, 0x0, 0x0, 0xB, 0xA, 0x2};
+  Value value(std::move(original_blob));
+
+  Value::BlobStorage new_blob = {0x0, 0x3, 0x0};
+  value.GetBlob() = new_blob;
+  EXPECT_EQ(new_blob, value.GetBlob());
+}
+
 #if BUILDFLAG(ENABLE_BASE_TRACING)
 TEST(ValuesTest, TracingSupport) {
   EXPECT_EQ(perfetto::TracedValueToString(Value(false)), "false");
diff --git a/base/win/registry.h b/base/win/registry.h
index 3b40f547..bf9f4cc 100644
--- a/base/win/registry.h
+++ b/base/win/registry.h
@@ -129,7 +129,7 @@
 
   // Setters:
 
-  // Sets an int32_t value.
+  // Sets a uint32_t value.
   LONG WriteValue(const wchar_t* name, DWORD in_value);
 
   // Sets a string value.
diff --git a/build/android/PRESUBMIT.py b/build/android/PRESUBMIT.py
index ddcb192f..027fd4f 100644
--- a/build/android/PRESUBMIT.py
+++ b/build/android/PRESUBMIT.py
@@ -91,6 +91,7 @@
           unit_tests=[
               J('.', 'list_class_verification_failures_test.py'),
               J('.', 'convert_dex_profile_tests.py'),
+              J('gyp', 'compile_java_tests.py'),
               J('gyp', 'create_unwind_table_tests.py'),
               J('gyp', 'dex_test.py'),
               J('gyp', 'extract_unwind_tables_tests.py'),
diff --git a/build/android/gyp/compile_java.py b/build/android/gyp/compile_java.py
index 6d58c2c..5492301 100755
--- a/build/android/gyp/compile_java.py
+++ b/build/android/gyp/compile_java.py
@@ -309,7 +309,6 @@
 def CreateJarFile(jar_path,
                   classes_dir,
                   services_map=None,
-                  service_provider_configuration_dir=None,
                   additional_jar_files=None,
                   extra_classes_jar=None):
   """Zips files from compilation into a single jar."""
@@ -317,13 +316,6 @@
   with action_helpers.atomic_output(jar_path) as f:
     with zipfile.ZipFile(f.name, 'w') as z:
       zip_helpers.zip_directory(z, classes_dir)
-      if service_provider_configuration_dir:
-        config_files = build_utils.FindInDirectory(
-            service_provider_configuration_dir)
-        for config_file in config_files:
-          zip_path = os.path.relpath(config_file,
-                                     service_provider_configuration_dir)
-          zip_helpers.add_to_zip_hermetic(z, zip_path, src_path=config_file)
       if services_map:
         for service_class, impl_classes in sorted(services_map.items()):
           zip_path = 'META-INF/services/' + service_class
@@ -344,7 +336,7 @@
 _PACKAGE_RE = re.compile(r'^package\s+(.*?)(;|\s*$)', flags=re.MULTILINE)
 
 _SERVICE_IMPL_RE = re.compile(
-    r'^(\s*)@ServiceImpl\(\s*(.+?)\.class\).*?\sclass\s+(\w+)',
+    r'^([\t ]*)@ServiceImpl\(\s*(.+?)\.class\).*?\sclass\s+(\w+)',
     flags=re.MULTILINE | re.DOTALL)
 
 # Finds all top-level classes (by looking for those that are not indented).
@@ -360,10 +352,8 @@
     flags=re.MULTILINE)
 
 
-def _ParseJavaSource(source_file, services_map):
+def ParseJavaSource(data, services_map):
   """This should support both Java and Kotlin files."""
-  data = pathlib.Path(source_file).read_text()
-
   package_name = ''
   if m := _PACKAGE_RE.search(data):
     package_name = m.group(1)
@@ -455,7 +445,8 @@
     logging.info('Collecting info file entries')
     entries = {}
     for path in itertools.chain(java_files, kt_files or []):
-      package_name, class_names = _ParseJavaSource(path, self.services_map)
+      data = pathlib.Path(path).read_text()
+      package_name, class_names = ParseJavaSource(data, self.services_map)
       source = self._srcjar_files.get(path, path)
       for fully_qualified_name in self._ProcessInfo(path, package_name,
                                                     class_names, source):
@@ -588,8 +579,6 @@
                                     options.jar_info_exclude_globs)
   try:
     classes_dir = os.path.join(temp_dir, 'classes')
-    service_provider_configuration = os.path.join(
-        temp_dir, 'service_provider_configuration')
 
     if java_files:
       os.makedirs(classes_dir)
@@ -630,16 +619,6 @@
                                            input_srcjars_dir)
       logging.info('Done extracting srcjars')
 
-    if options.header_jar:
-      logging.info('Extracting service provider configs')
-      # Extract META-INF/services/* so that it can be copied into the output
-      # .jar
-      build_utils.ExtractAll(options.header_jar,
-                             no_clobber=True,
-                             path=service_provider_configuration,
-                             pattern='META-INF/services/*')
-      logging.info('Done extracting service provider configs')
-
     if java_files:
       # Don't include the output directory in the initial set of args since it
       # being in a temp dir makes it unstable (breaks md5 stamping).
@@ -678,8 +657,7 @@
       metadata_parser.ParseAndWriteInfoFile(jar_info_path, java_files, kt_files)
 
     CreateJarFile(jar_path, classes_dir, metadata_parser.services_map,
-                  service_provider_configuration, options.additional_jar_files,
-                  options.kotlin_jar_path)
+                  options.additional_jar_files, options.kotlin_jar_path)
 
     # Remove input srcjars that confuse Android Studio:
     # https://crbug.com/353326240
diff --git a/build/android/gyp/compile_java_tests.py b/build/android/gyp/compile_java_tests.py
new file mode 100755
index 0000000..298610a20
--- /dev/null
+++ b/build/android/gyp/compile_java_tests.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+# 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.
+"""Tests for compile_java.py"""
+
+import collections
+import unittest
+
+import compile_java
+
+
+def _CreateData(class_annotation='',
+                class_prefix='',
+                nested_annotation='',
+                suffix=''):
+  return f"""\
+package pkg;
+
+import foo;
+import foo.Bar;
+import foo.Bar.Baz;
+
+{class_annotation}
+@SomeThing
+{class_prefix}class Foo {{
+  {nested_annotation}
+  public static class Nested {{ }}
+}}
+""" + suffix
+
+
+class CompileJavaTests(unittest.TestCase):
+
+  def do_service_loader_test(self, **kwargs):
+    data = _CreateData(**kwargs)
+    services_map = collections.defaultdict(list)
+    compile_java.ParseJavaSource(data, services_map)
+    return dict(services_map)
+
+  def do_classes_test(self, **kwargs):
+    data = _CreateData(**kwargs)
+    services_map = collections.defaultdict(list)
+    _, class_names = compile_java.ParseJavaSource(data, services_map)
+    return class_names
+
+  def testServiceImpl_NoUses(self):
+    services_map = self.do_service_loader_test()
+    self.assertEqual({}, services_map)
+
+  def testServiceImpl_LocalType(self):
+    services_map = self.do_service_loader_test(
+        class_annotation='@ServiceImpl(Local.class)')
+    self.assertEqual({'pkg.Local': ['pkg.Foo']}, services_map)
+
+  def testServiceImpl_ImportedTopType(self):
+    services_map = self.do_service_loader_test(
+        class_annotation='@ServiceImpl(Bar.class)')
+    self.assertEqual({'foo.Bar': ['pkg.Foo']}, services_map)
+
+  def testServiceImpl_ImportedNestedType1(self):
+    services_map = self.do_service_loader_test(
+        class_annotation='@ServiceImpl(Baz.class)')
+    self.assertEqual({'foo.Bar$Baz': ['pkg.Foo']}, services_map)
+
+  def testServiceImpl_ImportedNestedType2(self):
+    services_map = self.do_service_loader_test(
+        class_annotation='@ServiceImpl(Bar.Baz.class)')
+    self.assertEqual({'foo.Bar$Baz': ['pkg.Foo']}, services_map)
+
+  def testServiceImpl_NestedImpl(self):
+    services_map = self.do_service_loader_test(
+        class_annotation='@ServiceImpl(Baz.class)',
+        nested_annotation='@ServiceImpl(Baz.class)')
+    self.assertEqual({'foo.Bar$Baz': ['pkg.Foo', 'pkg.Foo$Nested']},
+                     services_map)
+
+  def testParseClasses(self):
+    classes = self.do_classes_test(class_prefix='public final ',
+                                   suffix='\nprivate class Extra {}')
+    self.assertEqual(['Foo', 'Extra'], classes)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/build/config/siso/nasm_linux.star b/build/config/siso/nasm_linux.star
index 36b496b..9501511 100644
--- a/build/config/siso/nasm_linux.star
+++ b/build/config/siso/nasm_linux.star
@@ -6,19 +6,11 @@
 
 load("@builtin//path.star", "path")
 load("@builtin//struct.star", "module")
-load("./config.star", "config")
-load("./nasm_scandeps.star", "nasm_scandeps")
 
 def __filegroups(ctx):
     return {}
 
-def __nasm(ctx, cmd):
-    inputs = nasm_scandeps.scandeps(ctx, cmd)
-    ctx.actions.fix(inputs = cmd.inputs + inputs)
-
-__handlers = {
-    "nasm": __nasm,
-}
+__handlers = {}
 
 def __step_config(ctx, step_config):
     remote_run = True  # Turn this to False when you do file access trace.
@@ -34,7 +26,6 @@
             "exclude_input_patterns": [
                 "*.stamp",
             ],
-            "handler": "nasm",
             "remote": remote_run,
             # chromeos generates default.profraw?
             "ignore_extra_output_pattern": ".*default.profraw",
diff --git a/build/config/siso/nasm_scandeps.star b/build/config/siso/nasm_scandeps.star
deleted file mode 100644
index e783a423..0000000
--- a/build/config/siso/nasm_scandeps.star
+++ /dev/null
@@ -1,88 +0,0 @@
-# -*- bazel-starlark -*-
-# 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.
-"""Siso configuration for nasm scandeps.
-
-- https://chromium.googlesource.com/chromium/deps/nasm
-
-gn
-- https://chromium.googlesource.com/chromium/deps/nasm/+/refs/heads/main/nasm_assemble.gni
-
-sample command line:
-  command = python3 ../../build/gn_run_binary.py nasm -DPIC -felf64 -P ../../third_party/dav1d/config/linux/x64/config.asm -I../../third_party/dav1d/libdav1d/src// -I../../third_party/dav1d/config/linux/x64/ -I./ -I../../ -Igen/ -DSTACK_ALIGNMENT=16 -MD obj/third_party/dav1d/dav1d_asm/${source_name_part}.o.d -o obj/third_party/dav1d/dav1d_asm/${source_name_part}.o ${in}
-
-"""
-
-load("@builtin//struct.star", "module")
-load("@builtin//path.star", "path")
-
-def __scan_input(ctx, src, inc_dirs):
-    inputs = [src]
-    curdir = path.dir(src)
-    include_directive_len = len("%include \"")
-    for line in str(ctx.fs.read(src)).split("\n"):
-        if not line.startswith("%include \""):
-            continue
-        fname = line[include_directive_len:]
-        i = fname.index("\"")
-        fname = fname[:i]
-        for d in [curdir] + inc_dirs:
-            pathname = path.join(d, fname)
-            if ctx.fs.exists(pathname):
-                inputs.extend(__scan_input(ctx, pathname, inc_dirs))
-                break
-    return inputs
-
-def __scandeps(ctx, cmd):
-    nasm_args = []
-    for i, arg in enumerate(cmd.args):
-        if path.base(arg) == "nasm":
-            nasm_args = cmd.args[i + 1:]
-            break
-    inc_dirs = []
-    skip = False
-    flag = ""
-    sources = []
-    for i, arg in enumerate(nasm_args):
-        if flag == "-I":
-            inc_dirs.append(ctx.fs.canonpath(arg))
-            flag = ""
-            continue
-        elif flag == "-P":
-            sources.append(ctx.fs.canonpath(arg))
-            flag = ""
-            continue
-        elif skip:
-            skip = False
-            continue
-        elif arg == "-o":
-            skip = True
-            continue
-        elif arg == "-MD":
-            skip = True
-            continue
-        elif arg == "-I":
-            flag = arg
-            continue
-        elif arg == "-P":
-            flag = arg
-            continue
-        elif arg.startswith("-I"):
-            inc_dirs.append(ctx.fs.canonpath(arg[2:]))
-            continue
-        elif arg.startswith("-P"):
-            sources.append(ctx.fs.canonpath(arg[2:]))
-            continue
-        elif arg.startswith("-"):
-            continue
-        sources.append(ctx.fs.canonpath(arg))
-    inputs = []
-    for src in sources:
-        inputs.extend(__scan_input(ctx, src, inc_dirs))
-    return inputs
-
-nasm_scandeps = module(
-    "nasm_scandeps",
-    scandeps = __scandeps,
-)
diff --git a/build/config/unsafe_buffers_paths.txt b/build/config/unsafe_buffers_paths.txt
index bec2a8a1..48cec18 100644
--- a/build/config/unsafe_buffers_paths.txt
+++ b/build/config/unsafe_buffers_paths.txt
@@ -83,7 +83,6 @@
 -chromeos/ash/services/libassistant/
 -chromeos/components/kcer/kcer_nss
 -clank/
--codelabs/
 -components/allocation_recorder/
 -components/chromeos_camera
 -components/commerce/
@@ -146,3 +145,9 @@
 # Removing this directory may fail on the bot CI bot:
 # https://luci-milo.appspot.com/ui/p/chrome/builders/official/win-arm64-clang
 -chrome/installer/
+
+# //codelabs is a directory that contains examples for developers to modify as
+# they learn about chromium development. This is indefinitely opt-out because
+# it is not part of the main build, and we don't want developers to be blocked
+# by this check.
+-codelabs/
diff --git a/build/fuchsia/test/run_test.py b/build/fuchsia/test/run_test.py
index 94ad260b..549e8f02 100755
--- a/build/fuchsia/test/run_test.py
+++ b/build/fuchsia/test/run_test.py
@@ -48,9 +48,9 @@
     if runner_args.test_type in ['gpu', 'perf']:
         return TelemetryTestRunner(runner_args.test_type, runner_args.out_dir,
                                    test_args, runner_args.target_id)
-    if runner_args.test_type in ['webpage']:
+    if runner_args.test_type == 'webpage':
         return WebpageTestRunner(runner_args.out_dir, test_args,
-                                 runner_args.target_id)
+                                 runner_args.target_id, runner_args.logs_dir)
     return create_executable_test_runner(runner_args, test_args)
 
 
diff --git a/build/fuchsia/test/run_webpage_test.py b/build/fuchsia/test/run_webpage_test.py
index 26c1f69..18450364 100644
--- a/build/fuchsia/test/run_webpage_test.py
+++ b/build/fuchsia/test/run_webpage_test.py
@@ -3,51 +3,67 @@
 # found in the LICENSE file.
 """Implements commands for running webpage tests."""
 
-import argparse
-import logging
+import os
+import subprocess
+import time
 
+from contextlib import suppress
 from typing import List, Optional
 
-from common import catch_sigterm, run_continuous_ffx_command, wait_for_sigterm
+import browser_runner
+from common import catch_sigterm, wait_for_sigterm
 from test_runner import TestRunner
 
+_DEVTOOLS_PORT_FILE = 'webpage_test_runner.devtools.port'
+
+
+def capture_devtools_port(proc: subprocess.Popen, logs_dir: str) -> int:
+    """Returns the devtools port initiated by the running |proc|. This
+    function should only be used when the WebpageTestRunner is executed by a
+    different process."""
+    port_file = os.path.join(logs_dir, _DEVTOOLS_PORT_FILE)
+
+    def try_reading_port():
+        if not os.path.isfile(port_file):
+            return None
+        with open(port_file, encoding='utf-8') as inp:
+            return int(inp.read())
+
+    while True:
+        result = try_reading_port()
+        if result:
+            return result
+        proc.poll()
+        assert not proc.returncode, 'Process stopped.'
+        time.sleep(1)
+
 
 class WebpageTestRunner(TestRunner):
     """Test runner for running GPU tests."""
 
     def __init__(self, out_dir: str, test_args: List[str],
-                 target_id: Optional[str]) -> None:
-        parser = argparse.ArgumentParser()
-        parser.add_argument(
-            '--browser',
-            choices=['web-engine-shell', 'chrome'],
-            help='The browser to use for Telemetry based tests.')
-        args, _ = parser.parse_known_args(test_args)
-
-        if args.browser == 'web-engine-shell':
-            packages = ['web_engine_shell']
+                 target_id: Optional[str], logs_dir: Optional[str]) -> None:
+        super().__init__(out_dir, test_args, ['web_engine_shell'], target_id)
+        self._runner = browser_runner.BrowserRunner(
+            browser_runner.WEB_ENGINE_SHELL, target_id, out_dir)
+        if logs_dir:
+            self.port_file = os.path.join(logs_dir, _DEVTOOLS_PORT_FILE)
         else:
-            packages = ['chrome']
-
-        super().__init__(out_dir, test_args, packages, target_id)
+            self.port_file = None
 
     def run_test(self):
         catch_sigterm()
-        browser_cmd = [
-            'test',
-            'run',
-            '-t',
-            '3600',  # Keep the webpage running for an hour.
-            f'fuchsia-pkg://fuchsia.com/{self._packages[0]}#meta/'
-            f'{self._packages[0]}.cm'
-        ]
-        browser_cmd.extend(
-            ['--', '--web-engine-package-name=web_engine_with_webui'])
-        if self._test_args:
-            browser_cmd.extend(self._test_args)
-        logging.info('Starting %s', self._packages[0])
-        browser_proc = run_continuous_ffx_command(browser_cmd)
+        self._runner.start()
+        if self.port_file:
+            with open(self.port_file, 'w') as out:
+                out.write(str(self._runner.devtools_port))
+        else:
+            print('DevTools is running on ' + str(self._runner.devtools_port))
         try:
             wait_for_sigterm('shutting down the webpage.')
         finally:
-            browser_proc.kill()
+            self._runner.close()
+            if self.port_file:
+                with suppress(OSError):
+                    os.remove(self.port_file)
+        return subprocess.CompletedProcess(args='', returncode=0)
diff --git a/cc/paint/paint_worklet_input.cc b/cc/paint/paint_worklet_input.cc
index d3b90e0c..2d2ad05d 100644
--- a/cc/paint/paint_worklet_input.cc
+++ b/cc/paint/paint_worklet_input.cc
@@ -18,6 +18,14 @@
 
 PaintWorkletInput::PropertyKey::PropertyKey(const PropertyKey& other) = default;
 
+PaintWorkletInput::PropertyKey::PropertyKey(PropertyKey&& other) = default;
+
+PaintWorkletInput::PropertyKey& PaintWorkletInput::PropertyKey::operator=(
+    const PropertyKey& other) = default;
+
+PaintWorkletInput::PropertyKey& PaintWorkletInput::PropertyKey::operator=(
+    PropertyKey&& other) = default;
+
 PaintWorkletInput::PropertyKey::~PropertyKey() = default;
 
 bool PaintWorkletInput::PropertyKey::operator==(
diff --git a/cc/paint/paint_worklet_input.h b/cc/paint/paint_worklet_input.h
index 7cd5a69..4f4ef9f 100644
--- a/cc/paint/paint_worklet_input.h
+++ b/cc/paint/paint_worklet_input.h
@@ -49,6 +49,9 @@
     PropertyKey(const std::string& custom_property_name, ElementId element_id);
     PropertyKey(NativePropertyType native_property_type, ElementId element_id);
     PropertyKey(const PropertyKey&);
+    PropertyKey(PropertyKey&&);
+    PropertyKey& operator=(PropertyKey&&);
+    PropertyKey& operator=(const PropertyKey&);
     ~PropertyKey();
 
     bool operator==(const PropertyKey& other) const;
diff --git a/chrome/VERSION b/chrome/VERSION
index c36c285..af966dc 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=130
 MINOR=0
-BUILD=6713
+BUILD=6714
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index d04ad9e..56823b62 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1305,6 +1305,7 @@
       "//components/offline_items_collection/core:core_java",
       "//components/omnibox/browser:browser_java",
       "//components/omnibox/browser:junit",
+      "//components/omnibox/common:junit",
       "//components/optimization_guide/proto:optimization_guide_proto_java",
       "//components/page_info/android:java",
       "//components/page_info/core:proto_java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
index 5f9f56145..be42780 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
@@ -16,7 +16,6 @@
 import androidx.viewpager.widget.ViewPager;
 
 import org.chromium.base.TraceEvent;
-import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManagerFactory;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
@@ -31,6 +30,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.autofill.AutofillDelegate;
 import org.chromium.components.autofill.AutofillSuggestion;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.ui.AsyncViewProvider;
 import org.chromium.ui.AsyncViewStub;
 import org.chromium.ui.ViewProvider;
@@ -194,7 +194,7 @@
                                 personalDataManager,
                                 suggestion.getCustomIconUrl(),
                                 suggestion.getIconId(),
-                                AutofillUiUtils.CardIconSize.SMALL,
+                                ImageSize.SMALL,
                                 /* showCustomIcon= */ true);
         return uiConfiguration;
     }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetCoordinator.java
index 5668e66..5462e97 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetCoordinator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetCoordinator.java
@@ -14,7 +14,6 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView;
 
-import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManagerFactory;
 import org.chromium.chrome.browser.keyboard_accessory.AccessoryAction;
@@ -22,6 +21,7 @@
 import org.chromium.chrome.browser.keyboard_accessory.R;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabItemsModel.AccessorySheetDataPiece.Type;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.autofill.ImageSize;
 
 /**
  * This component is a tab that can be added to the ManualFillingCoordinator. This tab allows
@@ -85,7 +85,7 @@
                                 info.getIconUrl(),
                                 CreditCardAccessorySheetViewBinder.getDrawableForOrigin(
                                         info.getOrigin()),
-                                AutofillUiUtils.CardIconSize.SMALL,
+                                ImageSize.SMALL,
                                 /* showCustomIcon= */ true);
         return uiConfiguration;
     }
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_group_row.xml b/chrome/android/features/tab_ui/java/res/layout/tab_group_row.xml
index eac8241..a8337b6 100644
--- a/chrome/android/features/tab_ui/java/res/layout/tab_group_row.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/tab_group_row.xml
@@ -16,7 +16,7 @@
   <include layout="@layout/tab_group_favicon_cluster" />
 
   <Space
-    android:layout_width="24dp"
+    android:layout_width="16dp"
     android:layout_height="match_parent" />
 
   <LinearLayout
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
index 4c81872..46afbf2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
@@ -39,6 +39,7 @@
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.Tracker;
+import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
 
 import java.util.List;
@@ -161,6 +162,17 @@
                 || type == UiType.CUSTOM_MESSAGE;
     }
 
+    boolean hasCollaboration(@Nullable RecyclerView.ViewHolder viewHolder) {
+        if (viewHolder instanceof SimpleRecyclerViewAdapter.ViewHolder simpleViewHolder) {
+            PropertyModel model = simpleViewHolder.model;
+            if (model.get(CARD_TYPE) == TAB) {
+                return TabShareUtils.isCollaborationIdValid(
+                        model.get(TabProperties.COLLABORATION_ID));
+            }
+        }
+        return false;
+    }
+
     @Override
     public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
         final int dragFlags = isMessageType(viewHolder) ? 0 : mDragFlags;
@@ -188,6 +200,10 @@
                 || target.getItemViewType() == TabProperties.UiType.CUSTOM_MESSAGE) {
             return false;
         }
+        // Collaborations cannot be dropped as it would destroy the collaboration.
+        if (hasCollaboration(current)) {
+            return false;
+        }
         return super.canDropOver(recyclerView, current, target);
     }
 
@@ -460,14 +476,13 @@
             RecyclerView.ViewHolder hoveredViewHolder =
                     mRecyclerView.findViewHolderForAdapterPosition(mHoveredTabIndex);
 
-            if (hoveredViewHolder instanceof SimpleRecyclerViewAdapter.ViewHolder
-                    && !hasTabPropertiesModel(hoveredViewHolder)) {
-                mHoveredTabIndex = TabModel.INVALID_TAB_INDEX;
-            } else {
+            if (hasTabPropertiesModel(hoveredViewHolder) && !hasCollaboration(viewHolder)) {
                 mModel.updateHoveredTabForMergeToGroup(mHoveredTabIndex, true);
-                if (prev_hovered != mHoveredTabIndex) {
-                    mModel.updateHoveredTabForMergeToGroup(prev_hovered, false);
-                }
+            } else {
+                mHoveredTabIndex = TabModel.INVALID_TAB_INDEX;
+            }
+            if (prev_hovered != mHoveredTabIndex) {
+                mModel.updateHoveredTabForMergeToGroup(prev_hovered, false);
             }
         } else if (actionState == ItemTouchHelper.ACTION_STATE_DRAG
                 && mTabGridDialogHandler != null) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewRenderTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewRenderTest.java
index b8821f15..1776c96 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewRenderTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewRenderTest.java
@@ -69,7 +69,7 @@
     public ChromeRenderTestRule mRenderTestRule =
             ChromeRenderTestRule.Builder.withPublicCorpus()
                     .setBugComponent(Component.UI_BROWSER_MOBILE_TAB_GROUPS)
-                    .setRevision(1)
+                    .setRevision(2)
                     .build();
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorGroupAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorGroupAction.java
index 6750aad..783b075 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorGroupAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorGroupAction.java
@@ -7,15 +7,19 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 
+import androidx.annotation.Nullable;
 import androidx.appcompat.content.res.AppCompatResources;
 
+import org.chromium.base.Token;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiMetricsHelper.TabListEditorActionMetricGroups;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -63,6 +67,11 @@
         mTabGroupCreationDialogManager = tabGroupCreationDialogManager;
     }
 
+    /**
+     * Called when the selected set of tabs changes. Each tab or tab group is represented by a
+     * single Tab ID. For tabs this is just the tab's ID, for a tab group one of the tabs in the
+     * group has its Tab ID used.
+     */
     @Override
     public void onSelectionStateChange(List<Integer> tabIds) {
         int tabCount =
@@ -70,14 +79,18 @@
                         ? getTabCountIncludingRelatedTabs(getTabGroupModelFilter(), tabIds)
                         : tabIds.size();
 
-        boolean isEnabled = false;
+        TabGroupModelFilter filter = getTabGroupModelFilter();
+        TabModel tabModel = filter.getTabModel();
+
+        boolean isEnabled = true;
         int tabIdsSize = tabIds.size();
-        if (tabIdsSize == 1) {
-            TabGroupModelFilter filter = getTabGroupModelFilter();
-            Tab tab = filter.getTabModel().getTabById(tabIds.get(0));
+        if (tabIdsSize == 0) {
+            isEnabled = false;
+        } else if (tabIdsSize == 1) {
+            Tab tab = tabModel.getTabById(tabIds.get(0));
             isEnabled = tab != null && !filter.isTabInTabGroup(tab);
         } else {
-            isEnabled = tabIdsSize > 1;
+            isEnabled = !hasMultipleCollaborations(tabModel, tabIds);
         }
         setEnabledAndItemCount(isEnabled, tabCount);
     }
@@ -147,33 +160,76 @@
 
     /**
      * Finds the tab to merge to. If at least one group is selected, merge all selected items to the
-     * group with the smallest group index. Otherwise, all selected items are merge to the tab with
-     * the largest tab index.
-     * @param tabs the list of all tabs to merge.
+     * group with the smallest group index or the collaboration if only one collaboration is
+     * selected. Otherwise, all selected items are merge to the tab with the largest tab index.
+     *
+     * @param tabs the list of all tabs to merge, this includes all tabs for tab groups.
      * @param filter the {@link TabGroupModelFilter} for managing groups.
      * @param actionOnRelatedTabs whether to attempt to merge to groups.
      * @return the tab to merge to.
      */
     private Tab getDestinationTab(
-            List<Tab> tabs,
-            TabGroupModelFilter filter,
-            boolean actionOnRelatedTabs) {
+            List<Tab> tabs, TabGroupModelFilter filter, boolean actionOnRelatedTabs) {
         TabModel model = filter.getTabModel();
+        @Nullable
+        TabGroupSyncService tabGroupSyncService =
+                model.isIncognitoBranded()
+                        ? null
+                        : TabGroupSyncServiceFactory.getForProfile(model.getProfile());
+
+        @Nullable Token collaborationTabGroupId = null;
         int greatestTabIndex = TabModel.INVALID_TAB_INDEX;
-        int smallestGroupIndex = TabModel.INVALID_TAB_INDEX;
+        int groupIndex = TabModel.INVALID_TAB_INDEX;
         for (Tab tab : tabs) {
             final int index = TabModelUtils.getTabIndexById(model, tab.getId());
             greatestTabIndex = Math.max(index, greatestTabIndex);
             if (actionOnRelatedTabs && filter.isTabInTabGroup(tab)) {
-                smallestGroupIndex =
-                        (smallestGroupIndex == TabModel.INVALID_TAB_INDEX)
-                                ? index
-                                : Math.min(index, smallestGroupIndex);
+                if (TabShareUtils.isCollaborationIdValid(
+                        TabShareUtils.getCollaborationIdOrNull(
+                                tab.getId(), model, tabGroupSyncService))) {
+                    if (collaborationTabGroupId == null) {
+                        groupIndex = index;
+                        collaborationTabGroupId = tab.getTabGroupId();
+                    } else if (collaborationTabGroupId.equals(tab.getTabGroupId())) {
+                        groupIndex = Math.min(index, groupIndex);
+                    } else {
+                        assert false : "Merging multiple collaborations is not allowed.";
+                    }
+                } else if (groupIndex == TabModel.INVALID_TAB_INDEX) {
+                    groupIndex = index;
+                } else if (collaborationTabGroupId == null) {
+                    groupIndex = Math.min(index, groupIndex);
+                }
             }
         }
         return model.getTabAt(
-                (smallestGroupIndex != TabModel.INVALID_TAB_INDEX)
-                        ? smallestGroupIndex
-                        : greatestTabIndex);
+                (groupIndex != TabModel.INVALID_TAB_INDEX) ? groupIndex : greatestTabIndex);
+    }
+
+    /**
+     * Computes whether multiple collaborations are selected.
+     *
+     * @param tabModel The {@link TabModel} to use for checking.
+     * @param tabIds The list of Tab IDs to check for collaboration membership. For tab groups only
+     *     a single tab ID for one of the members of the tab group is provided.
+     */
+    private boolean hasMultipleCollaborations(TabModel tabModel, List<Integer> tabIds) {
+        if (tabModel.isIncognitoBranded()) return false;
+
+        @Nullable
+        TabGroupSyncService tabGroupSyncService =
+                TabGroupSyncServiceFactory.getForProfile(tabModel.getProfile());
+        if (tabGroupSyncService == null) return false;
+
+        boolean foundCollaboration = false;
+        for (int tabId : tabIds) {
+            if (TabShareUtils.isCollaborationIdValid(
+                    TabShareUtils.getCollaborationIdOrNull(tabId, tabModel, tabGroupSyncService))) {
+                if (foundCollaboration) return true;
+
+                foundCollaboration = true;
+            }
+        }
+        return false;
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
index a1c87e6e..3397607 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
@@ -53,6 +53,10 @@
 
     public static final WritableIntPropertyKey TAB_ID = new WritableIntPropertyKey();
 
+    // TODO(crbug.com/365972761): Move this to `TabGroupProperties` when it is created.
+    public static final WritableObjectPropertyKey<String> COLLABORATION_ID =
+            new WritableObjectPropertyKey<>();
+
     public static final ReadableBooleanPropertyKey IS_INCOGNITO = new ReadableBooleanPropertyKey();
 
     public static final WritableObjectPropertyKey<TabActionListener> TAB_CLICK_LISTENER =
@@ -64,6 +68,7 @@
     public static final WritableObjectPropertyKey<TabActionButtonData> TAB_ACTION_BUTTON_DATA =
             new WritableObjectPropertyKey<>();
 
+    // TODO(crbug.com/365973166): Move this to `TabStripProperties` when it is created.
     /**
      * Indicator that a {@link TabProperties.FAVICON_FETCHER} has completed fetching a favicon. Only
      * used by TabStrip for the {@link TabStripSnapshotter}.
@@ -96,14 +101,15 @@
     public static final WritableObjectPropertyKey<AccessibilityDelegate> ACCESSIBILITY_DELEGATE =
             new WritableObjectPropertyKey<>();
 
-    public static final WritableObjectPropertyKey<ShoppingPersistedTabDataFetcher>
-            SHOPPING_PERSISTED_TAB_DATA_FETCHER = new WritableObjectPropertyKey<>(true);
-
     public static final WritableObjectPropertyKey<String> CONTENT_DESCRIPTION_STRING =
             new WritableObjectPropertyKey<>();
 
     public static final WritableObjectPropertyKey<String> ACTION_BUTTON_DESCRIPTION_STRING =
             new WritableObjectPropertyKey<>();
+
+    public static final WritableObjectPropertyKey<ShoppingPersistedTabDataFetcher>
+            SHOPPING_PERSISTED_TAB_DATA_FETCHER = new WritableObjectPropertyKey<>(true);
+
     public static final WritableBooleanPropertyKey SHOULD_SHOW_PRICE_DROP_TOOLTIP =
             new WritableBooleanPropertyKey();
 
@@ -115,9 +121,11 @@
     public static final WritableBooleanPropertyKey USE_SHRINK_CLOSE_ANIMATION =
             new WritableBooleanPropertyKey();
 
+    // TODO(crbug.com/365972761): Move this to `TabGroupProperties` when it is created.
     /** The {@link TabGroupColorId} for a tab group representation's color in TabListMode only. */
     public static final WritableIntPropertyKey TAB_GROUP_COLOR_ID = new WritableIntPropertyKey();
 
+    // TODO(crbug.com/365973166): Move this to `TabStripProperties` when it is created.
     public static final WritableBooleanPropertyKey HAS_NOTIFICATION_BUBBLE =
             new WritableBooleanPropertyKey();
 
@@ -128,6 +136,7 @@
             new PropertyKey[] {
                 TAB_ACTION_STATE,
                 TAB_ID,
+                COLLABORATION_ID,
                 IS_INCOGNITO,
                 TAB_CLICK_LISTENER,
                 TAB_LONG_CLICK_LISTENER,
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
index ff181e5..be26f49a 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallbackUnitTest.java
@@ -62,9 +62,9 @@
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.ui.modelutil.MVCListAdapter;
-import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
+import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter.ViewHolder;
 
 /** Tests for {@link TabGridItemTouchHelperCallback}. */
 @SuppressWarnings("ResultOfMethodCallIgnored")
@@ -76,6 +76,8 @@
         })
 public class TabGridItemTouchHelperCallbackUnitTest {
 
+    private static final String COLLABORATION_ID_1 = "COLLABORATION_ID_1";
+    private static final String COLLABORATION_ID_2 = "COLLABORATION_ID_2";
     private static final String TAB1_TITLE = "Tab1";
     private static final String TAB2_TITLE = "Tab2";
     private static final String TAB3_TITLE = "Tab3";
@@ -111,12 +113,11 @@
     private final ObservableSupplierImpl<TabModelFilter> mTabModelFilterSupplier =
             new ObservableSupplierImpl<>();
 
-    private SimpleRecyclerViewAdapter.ViewHolder mMockViewHolder1;
-    private SimpleRecyclerViewAdapter.ViewHolder mMockViewHolder2;
-    private RecyclerView.ViewHolder mFakeViewHolder1;
-    private RecyclerView.ViewHolder mFakeViewHolder2;
-    private RecyclerView.ViewHolder mFakeViewHolder3;
-    private RecyclerView.ViewHolder mFakeViewHolder4;
+    private SimpleRecyclerViewAdapter mSimpleAdapter;
+    private ViewHolder mMockViewHolder1;
+    private ViewHolder mMockViewHolder2;
+    private ViewHolder mMockViewHolder3;
+    private ViewHolder mMockViewHolder4;
     private View mItemView1;
     private View mItemView2;
     private View mItemView3;
@@ -135,22 +136,19 @@
                 .when(mRecyclerView)
                 .post(any());
 
+        mModel = new TabListModel();
+        mSimpleAdapter = new SimpleRecyclerViewAdapter(mModel);
+
         Tab tab1 = prepareTab(TAB1_ID, TAB1_TITLE);
         Tab tab2 = prepareTab(TAB2_ID, TAB2_TITLE);
         Tab tab3 = prepareTab(TAB3_ID, TAB3_TITLE);
         Tab tab4 = prepareTab(TAB4_ID, TAB4_TITLE);
-        mMockViewHolder1 = prepareMockViewHolder(TAB1_ID, POSITION1);
-        mMockViewHolder2 = prepareMockViewHolder(TAB2_ID, POSITION2);
         // Mock four cards in a grid layout. Each card is of width 4 and height 4. Both the side
         // gaps and top gaps between adjacent cards are 1.
         mItemView1 = prepareItemView(0, 0, 4, 4);
         mItemView2 = prepareItemView(5, 0, 9, 4);
         mItemView3 = prepareItemView(0, 5, 4, 9);
         mItemView4 = prepareItemView(5, 5, 9, 9);
-        mFakeViewHolder1 = prepareFakeViewHolder(mItemView1);
-        mFakeViewHolder2 = prepareFakeViewHolder(mItemView2);
-        mFakeViewHolder3 = prepareFakeViewHolder(mItemView3);
-        mFakeViewHolder4 = prepareFakeViewHolder(mItemView4);
 
         mTabModelFilterSupplier.set(mTabGroupModelFilter);
         doReturn(mProfile).when(mTabModel).getProfile();
@@ -168,9 +166,10 @@
         doReturn(TAB2_ID).when(tab2).getRootId();
         doReturn(TAB3_ID).when(tab3).getRootId();
         doReturn(TAB4_ID).when(tab4).getRootId();
+        initAndAssertAllProperties();
+
         setupRecyclerView();
 
-        mModel = new TabListModel();
         setupItemTouchHelperCallback(false);
         TrackerFactory.setTrackerForTests(mTracker);
     }
@@ -190,13 +189,13 @@
         doReturn(mRecyclerView).when(mItemView3).getParent();
         doReturn(mRecyclerView).when(mItemView4).getParent();
         when(mRecyclerView.findViewHolderForAdapterPosition(POSITION1))
-                .thenReturn(mFakeViewHolder1);
+                .thenReturn(mMockViewHolder1);
         when(mRecyclerView.findViewHolderForAdapterPosition(POSITION2))
-                .thenReturn(mFakeViewHolder2);
+                .thenReturn(mMockViewHolder2);
         when(mRecyclerView.findViewHolderForAdapterPosition(POSITION3))
-                .thenReturn(mFakeViewHolder3);
+                .thenReturn(mMockViewHolder3);
         when(mRecyclerView.findViewHolderForAdapterPosition(POSITION4))
-                .thenReturn(mFakeViewHolder4);
+                .thenReturn(mMockViewHolder4);
     }
 
     private void setupItemTouchHelperCallback(boolean isDialog) {
@@ -218,14 +217,7 @@
     }
 
     @Test
-    public void initializesWithCurrentTabs() {
-        initAndAssertAllProperties();
-    }
-
-    @Test
     public void onStartDraggingTab() {
-        initAndAssertAllProperties();
-
         assertThat(
                 mModel.get(0).model.get(TabProperties.CARD_ANIMATION_STATUS),
                 equalTo(TabGridView.AnimationStatus.CARD_RESTORE));
@@ -242,17 +234,13 @@
 
     @Test
     public void onSwipeTab_Delete() {
-        initAndAssertAllProperties();
-
         mItemTouchHelperCallback.onSwiped(mMockViewHolder1, POSITION1);
 
-        verify(mTabClosedListener).run(null, TAB1_ID);
+        verify(mTabClosedListener).run(mItemView1, TAB1_ID);
     }
 
     @Test
     public void onReleaseTab_NoMerge() {
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#1 in TabListModel.
         mModel.get(0)
                 .model
@@ -277,8 +265,6 @@
 
     @Test
     public void onReleaseTab_MergeBackward() {
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#1 in TabListModel.
         mModel.get(0)
                 .model
@@ -309,8 +295,6 @@
 
     @Test
     public void onReleaseTab_MergeForward() {
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#2 in TabListModel.
         mModel.get(1)
                 .model
@@ -341,8 +325,6 @@
 
     @Test
     public void onReleaseTab_Merge_CleanOut() {
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#2 in TabListModel.
         mItemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION2);
 
@@ -362,8 +344,6 @@
 
     @Test
     public void onReleaseTab_Merge_Scrolling() {
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#2 in TabListModel.
         mItemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION2);
 
@@ -383,7 +363,6 @@
 
     @Test
     public void onReleaseTab_UngroupBar_Hide() {
-        initAndAssertAllProperties();
         setupItemTouchHelperCallback(true);
 
         mItemTouchHelperCallback.onSelectedChanged(
@@ -395,8 +374,6 @@
 
     @Test
     public void onReleaseTab_Ungroup() {
-        initAndAssertAllProperties();
-
         setupItemTouchHelperCallback(true);
         mItemTouchHelperCallback.setUnGroupTabIndexForTesting(POSITION1);
 
@@ -411,8 +388,6 @@
 
     @Test
     public void onReleaseTab_Ungroup_Scrolling() {
-        initAndAssertAllProperties();
-
         setupItemTouchHelperCallback(true);
         mItemTouchHelperCallback.setUnGroupTabIndexForTesting(POSITION1);
 
@@ -430,8 +405,6 @@
 
     @Test
     public void onReleaseTab_Ungroup_CleanOut() {
-        initAndAssertAllProperties();
-
         setupItemTouchHelperCallback(true);
         mItemTouchHelperCallback.setUnGroupTabIndexForTesting(POSITION1);
 
@@ -448,19 +421,43 @@
     }
 
     @Test
-    public void onDragTab_Hovered_GTS_Horizontal() {
-        initAndAssertAllProperties();
+    public void onDragTab_Hovered_GTS_OneCollaborationCannotDrop() {
+        mMockViewHolder1.model.set(TabProperties.COLLABORATION_ID, COLLABORATION_ID_1);
+        // Drag card#1 rightwards to hover on card#2. We cannot drop a collaboration over a normal
+        // tab or it will be destroyed.
+        verifyDrag(mMockViewHolder1, 5, 0, POSITION2, TabGridView.AnimationStatus.CARD_RESTORE);
+        // Drag card#2 leftwards to hover on card#1. This is still allowed as we can add tabs to a
+        // collaboration.
+        verifyDrag(
+                mMockViewHolder2,
+                -5,
+                0,
+                POSITION1,
+                TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
+    }
 
+    @Test
+    public void onDragTab_Hovered_GTS_TwoCollaborationCannotDrop() {
+        mMockViewHolder1.model.set(TabProperties.COLLABORATION_ID, COLLABORATION_ID_1);
+        mMockViewHolder2.model.set(TabProperties.COLLABORATION_ID, COLLABORATION_ID_2);
+
+        // Merging collaborations is not allowed. Neither of these should work.
+        verifyDrag(mMockViewHolder1, 5, 0, POSITION2, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder2, -5, 0, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+    }
+
+    @Test
+    public void onDragTab_Hovered_GTS_Horizontal() {
         // Drag card#1 rightwards to hover on card#2.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 5,
                 0,
                 POSITION2,
                 TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
         // Drag card#2 leftwards to hover on card#1.
         verifyDrag(
-                mFakeViewHolder2,
+                mMockViewHolder2,
                 -5,
                 0,
                 POSITION1,
@@ -469,18 +466,16 @@
 
     @Test
     public void onDragTab_Hovered_GTS_Vertical() {
-        initAndAssertAllProperties();
-
         // Drag card#1 downwards to hover on card#3.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 0,
                 5,
                 POSITION3,
                 TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
         // Drag card#3 upwards to hover on card#1.
         verifyDrag(
-                mFakeViewHolder3,
+                mMockViewHolder3,
                 0,
                 -5,
                 POSITION1,
@@ -489,32 +484,30 @@
 
     @Test
     public void onDragTab_Hovered_GTS_Diagonal() {
-        initAndAssertAllProperties();
-
         // Drag card#1 diagonally to hover on card#4.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 5,
                 5,
                 POSITION4,
                 TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
         // Drag card#4 diagonally to hover on card#1.
         verifyDrag(
-                mFakeViewHolder4,
+                mMockViewHolder4,
                 -5,
                 -5,
                 POSITION1,
                 TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
         // Drag card#2 diagonally to hover on card#3.
         verifyDrag(
-                mFakeViewHolder2,
+                mMockViewHolder2,
                 -5,
                 5,
                 POSITION3,
                 TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
         // Drag card#3 diagonally to hover on card#2.
         verifyDrag(
-                mFakeViewHolder3,
+                mMockViewHolder3,
                 5,
                 -5,
                 POSITION2,
@@ -523,64 +516,56 @@
 
     @Test
     public void onDragTab_NotHovered_GTS_Horizontal() {
-        initAndAssertAllProperties();
-
         // With merge threshold equal to 50% of the overlapped area, the following dX should never
         // trigger hovering.
-        verifyDrag(mFakeViewHolder1, 3, 0, POSITION2, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder2, -3, 0, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 3, 0, POSITION2, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder2, -3, 0, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
         // With merge threshold equal to 50% of the overlapped area, the following dX should never
         // trigger hovering.
-        verifyDrag(mFakeViewHolder1, 7, 0, POSITION2, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder2, -7, 0, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 7, 0, POSITION2, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder2, -7, 0, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
     }
 
     @Test
     public void onDragTab_NotHovered_GTS_Vertical() {
-        initAndAssertAllProperties();
-
         // With merge threshold equal to 50% of the overlapped area, the following dX should never
         // trigger hovering.
-        verifyDrag(mFakeViewHolder1, 0, 3, POSITION3, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder3, 0, -3, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 0, 3, POSITION3, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder3, 0, -3, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
         // With merge threshold equal to 50% of the overlapped area, the following dX should never
         // trigger hovering.
-        verifyDrag(mFakeViewHolder1, 0, 7, POSITION3, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder3, 0, -7, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 0, 7, POSITION3, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder3, 0, -7, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
     }
 
     @Test
     public void onDragTab_NotHovered_GTS_Diagonal() {
-        initAndAssertAllProperties();
-
         // With merge threshold equal to 50% of the overlapped area, the following dX should never
         // trigger hovering.
-        verifyDrag(mFakeViewHolder1, 3, 4, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder1, 4, 3, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder4, -4, -3, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder4, -3, -4, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 3, 4, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 4, 3, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder4, -4, -3, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder4, -3, -4, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
         // With merge threshold equal to 50% of the overlapped area, the following dX should never
         // trigger hovering.
-        verifyDrag(mFakeViewHolder1, 7, 6, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder1, 6, 7, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder4, -6, -7, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder4, -7, -6, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 7, 6, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 6, 7, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder4, -6, -7, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder4, -7, -6, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
     }
 
     @Test
     public void onDragTab_Restore_Hovered_GTS() {
-        initAndAssertAllProperties();
-
         // Simulate the process of hovering card#1 on card#2.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 5,
                 0,
                 POSITION2,
                 TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
         // Continue to drag card#1 horizontally so that it is no longer hovering on card#2.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 10,
                 0,
                 POSITION2,
@@ -588,14 +573,14 @@
 
         // Simulate the process of hovering card#1 on card#3.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 0,
                 5,
                 POSITION3,
                 TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
         // Continue to drag card#1 vertically so that it is no longer hovering on card#3.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 0,
                 10,
                 POSITION3,
@@ -603,14 +588,14 @@
 
         // Simulate the process of hovering card#1 on card#4.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 5,
                 5,
                 POSITION4,
                 TabGridView.AnimationStatus.HOVERED_CARD_ZOOM_IN);
         // Continue to drag card#1 diagonally so that it is no longer hovering on card#4.
         verifyDrag(
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 10,
                 10,
                 POSITION4,
@@ -619,24 +604,22 @@
 
     @Test
     public void onDragTab_Hovered_NonGTS() {
-        initAndAssertAllProperties();
         // Suppose drag happens in components other than GTS.
         mItemTouchHelperCallback.setActionsOnAllRelatedTabsForTesting(false);
 
         // Hovering shouldn't make any difference.
-        verifyDrag(mFakeViewHolder1, 5, 0, POSITION2, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder2, -5, 0, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 5, 0, POSITION2, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder2, -5, 0, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
 
-        verifyDrag(mFakeViewHolder1, 0, 5, POSITION3, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder3, 0, -5, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 0, 5, POSITION3, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder3, 0, -5, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
 
-        verifyDrag(mFakeViewHolder1, 5, 5, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
-        verifyDrag(mFakeViewHolder4, -5, -5, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder1, 5, 5, POSITION4, TabGridView.AnimationStatus.CARD_RESTORE);
+        verifyDrag(mMockViewHolder4, -5, -5, POSITION1, TabGridView.AnimationStatus.CARD_RESTORE);
     }
 
     @Test
     public void onDragTab_Ungroup() {
-        initAndAssertAllProperties();
         setupItemTouchHelperCallback(true);
 
         // Simulate dragging card#1 down to the ungroup bar.
@@ -644,7 +627,7 @@
         mItemTouchHelperCallback.onChildDraw(
                 mCanvas,
                 mRecyclerView,
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 0,
                 7,
                 ItemTouchHelper.ACTION_STATE_DRAG,
@@ -657,7 +640,7 @@
         mItemTouchHelperCallback.onChildDraw(
                 mCanvas,
                 mRecyclerView,
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 0,
                 2,
                 ItemTouchHelper.ACTION_STATE_DRAG,
@@ -669,7 +652,6 @@
 
     @Test
     public void onDragTab_NotUngroup() {
-        initAndAssertAllProperties();
         setupItemTouchHelperCallback(true);
 
         // With recyclerview bottom equal to 12 and ungroup threshold equal to 2, any drag with
@@ -678,7 +660,7 @@
         mItemTouchHelperCallback.onChildDraw(
                 mCanvas,
                 mRecyclerView,
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 0,
                 6,
                 ItemTouchHelper.ACTION_STATE_DRAG,
@@ -689,7 +671,7 @@
         mItemTouchHelperCallback.onChildDraw(
                 mCanvas,
                 mRecyclerView,
-                mFakeViewHolder3,
+                mMockViewHolder3,
                 0,
                 1,
                 ItemTouchHelper.ACTION_STATE_DRAG,
@@ -704,7 +686,6 @@
 
     @Test
     public void onDragTab_AfterRelease() {
-        initAndAssertAllProperties();
         setupItemTouchHelperCallback(true);
 
         // Simulate that drop is finished, but there are some extra onChildDraw calls.
@@ -714,7 +695,7 @@
         mItemTouchHelperCallback.onChildDraw(
                 mCanvas,
                 mRecyclerView,
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 0,
                 8,
                 ItemTouchHelper.ACTION_STATE_DRAG,
@@ -724,7 +705,6 @@
     }
 
     private void clearViewBeforePost() {
-        initAndAssertAllProperties();
         setupItemTouchHelperCallback(false);
         // Mock that when the dragging animation ends, the recyclerView is in an inconsistent state:
         // recyclerView should be cleaned out, yet the animated view is stale.
@@ -732,10 +712,10 @@
         doReturn(1).when(mRecyclerView).getChildCount();
         doReturn(0).when(mAdapter).getItemCount();
         when(mItemView1.getParent()).thenReturn(mRecyclerView);
-        when(mFakeViewHolder1.getLayoutPosition()).thenReturn(POSITION1);
+        when(mMockViewHolder1.getLayoutPosition()).thenReturn(POSITION1);
         when(mRecyclerView.indexOfChild(mItemView1)).thenReturn(POSITION1);
 
-        mItemTouchHelperCallback.clearView(mRecyclerView, mFakeViewHolder1);
+        mItemTouchHelperCallback.clearView(mRecyclerView, mMockViewHolder1);
     }
 
     @Test
@@ -848,6 +828,23 @@
                         mRecyclerView, mMockViewHolder2, mMockViewHolder1));
     }
 
+    @Test
+    public void tabItemsAreDropable() {
+        setupItemTouchHelperCallback(false);
+        assertTrue(
+                mItemTouchHelperCallback.canDropOver(
+                        mRecyclerView, mMockViewHolder2, mMockViewHolder1));
+    }
+
+    @Test
+    public void collaborationCurrentIsNotDropable() {
+        setupItemTouchHelperCallback(false);
+        mMockViewHolder2.model.set(TabProperties.COLLABORATION_ID, COLLABORATION_ID_1);
+        assertFalse(
+                mItemTouchHelperCallback.canDropOver(
+                        mRecyclerView, mMockViewHolder2, mMockViewHolder1));
+    }
+
     @Test(expected = AssertionError.class)
     public void messageItemOnMoveFail() {
         when(mMockViewHolder1.getItemViewType()).thenReturn(TabProperties.UiType.MESSAGE);
@@ -888,8 +885,6 @@
 
     @Test
     public void onLongPress_blockNextAction() {
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#1 in TabListModel.
         mItemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION1);
 
@@ -901,8 +896,6 @@
 
     @Test
     public void onLongPressWithDrag_dontBlockNextAction() {
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#1 in TabListModel.
         mItemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION1);
 
@@ -910,7 +903,7 @@
         mItemTouchHelperCallback.onChildDraw(
                 mCanvas,
                 mRecyclerView,
-                mFakeViewHolder1,
+                mMockViewHolder1,
                 10,
                 5,
                 ItemTouchHelper.ACTION_STATE_DRAG,
@@ -926,8 +919,6 @@
     public void onLongPress_triggerTabListEditor() {
         TabUiFeatureUtilities.setTabListEditorLongPressEntryEnabledForTesting(true);
 
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#1 in TabListModel.
         mItemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION1);
 
@@ -942,8 +933,6 @@
     public void onLongPress_preventTriggerTabListEditor() {
         TabUiFeatureUtilities.setTabListEditorLongPressEntryEnabledForTesting(true);
 
-        initAndAssertAllProperties();
-
         // Simulate the selection of card#1 in TabListModel.
         mItemTouchHelperCallback.setSelectedTabIndexForTesting(POSITION1);
 
@@ -963,7 +952,6 @@
         ChromeFeatureList.TAB_GROUP_CREATION_DIALOG_ANDROID
     })
     public void onTabMergeToGroup_willMergingCreateNewGroup() {
-        initAndAssertAllProperties();
         doReturn(true).when(mTabGroupModelFilter).willMergingCreateNewGroup(any());
 
         // Simulate the selection of card#1 in TabListModel.
@@ -1027,16 +1015,18 @@
         assertThat(mModel.get(1).model.get(CARD_ALPHA), equalTo(1f));
         assertThat(mModel.get(2).model.get(CARD_ALPHA), equalTo(1f));
         assertThat(mModel.get(3).model.get(CARD_ALPHA), equalTo(1f));
+
+        mMockViewHolder1 = prepareMockViewHolder(mModel.get(0).model, mItemView1, POSITION1);
+        mMockViewHolder2 = prepareMockViewHolder(mModel.get(1).model, mItemView2, POSITION2);
+        mMockViewHolder3 = prepareMockViewHolder(mModel.get(2).model, mItemView3, POSITION3);
+        mMockViewHolder4 = prepareMockViewHolder(mModel.get(3).model, mItemView4, POSITION4);
     }
 
     private void addTabInfoModel(Tab tab) {
-        PropertyKey[] testKeysTabGrid =
-                new PropertyKey[] {
-                    TabProperties.TAB_ID, TabProperties.CARD_ANIMATION_STATUS, CARD_ALPHA, CARD_TYPE
-                };
         PropertyModel tabInfo =
-                new PropertyModel.Builder(testKeysTabGrid)
+                new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
                         .with(TabProperties.TAB_ID, tab.getId())
+                        .with(TabProperties.COLLABORATION_ID, null)
                         .with(
                                 TabProperties.CARD_ANIMATION_STATUS,
                                 TabGridView.AnimationStatus.CARD_RESTORE)
@@ -1053,14 +1043,11 @@
         return tab;
     }
 
-    private SimpleRecyclerViewAdapter.ViewHolder prepareMockViewHolder(int id, int position) {
-        SimpleRecyclerViewAdapter.ViewHolder viewHolder =
-                mock(SimpleRecyclerViewAdapter.ViewHolder.class);
-        viewHolder.model =
-                new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
-                        .with(TabProperties.TAB_ID, id)
-                        .with(CARD_TYPE, TAB)
-                        .build();
+    private ViewHolder prepareMockViewHolder(PropertyModel model, View itemView, int position) {
+        ViewHolder viewHolder = spy(mSimpleAdapter.new ViewHolder(itemView, /* binder= */ null));
+        when(viewHolder.getItemViewType()).thenReturn(TabProperties.UiType.TAB);
+        when(viewHolder.getAdapterPosition()).thenReturn(position);
+        viewHolder.model = model;
         return viewHolder;
     }
 
@@ -1074,8 +1061,4 @@
         doReturn(bottom - top).when(view).getHeight();
         return view;
     }
-
-    private RecyclerView.ViewHolder prepareFakeViewHolder(View itemView) {
-        return spy(new RecyclerView.ViewHolder(itemView) {});
-    }
 }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java
index d7058c8..6fab709 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorActionUnitTestHelper.java
@@ -6,10 +6,17 @@
 
 import static org.mockito.Mockito.when;
 
+import androidx.annotation.Nullable;
+
+import org.chromium.base.Token;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
+import org.chromium.components.tab_group_sync.LocalTabGroupId;
+import org.chromium.components.tab_group_sync.SavedTabGroup;
+import org.chromium.components.tab_group_sync.SavedTabGroupTab;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -21,22 +28,46 @@
 public class TabListEditorActionUnitTestHelper {
     /** Defines a group of tabs and its selection state. */
     public static class TabIdGroup {
-        private int[] mTabIds;
-        private boolean mSelected;
+        private final int[] mTabIds;
+        private final boolean mIsGroup;
+        private final boolean mSelected;
+        private final boolean mIsCollaboration;
 
         /**
          * @param tabIds the tab ids.
-         * @param selected whether the first tab in the group is selected.
+         * @param isGroup whether the tab is a group.
+         * @param selected whether the group is selected.
+         * @param isCollaboration whether the tab group is a collaboration.
          */
-        TabIdGroup(int[] tabIds, boolean selected) {
+        TabIdGroup(int[] tabIds, boolean isGroup, boolean selected, boolean isCollaboration) {
             mTabIds = tabIds;
+            mIsGroup = isGroup;
             mSelected = selected;
+            mIsCollaboration = isCollaboration;
         }
 
         boolean isSelected() {
             return mSelected;
         }
 
+        boolean isGroup() {
+            return mIsGroup;
+        }
+
+        @Nullable
+        String getCollaborationId() {
+            return mIsCollaboration ? getTabGroupId().toString() + "_collaboration" : null;
+        }
+
+        int getRootId() {
+            return mTabIds[0];
+        }
+
+        @Nullable
+        Token getTabGroupId() {
+            return mIsGroup ? new Token(1L, mTabIds[0]) : null;
+        }
+
         int[] getTabIds() {
             return mTabIds;
         }
@@ -82,8 +113,10 @@
 
     /**
      * Adds the tabs described tabs to mock objects to set up an Action unit test.
+     *
      * @param tabModel a {@link MockTabModel}.
      * @param filter a mocked {@link TabGroupModelFilter}.
+     * @param tabGroupSyncService a mocked {@link TabGroupSyncService}.
      * @param selectionDelegate a mocked {@link SelectionDelegate}.
      * @param tabIdGroups defining the tab structure.
      * @param deterministicSetOrder allow arbitrary selection order.
@@ -91,6 +124,7 @@
     public static TabListHolder configureTabs(
             MockTabModel tabModel,
             TabGroupModelFilter filter,
+            TabGroupSyncService tabGroupSyncService,
             SelectionDelegate<Integer> selectionDelegate,
             List<TabIdGroup> tabIdGroups,
             boolean deterministicSetOrder) {
@@ -98,15 +132,23 @@
         List<Tab> selectedAndRelatedTabs = new ArrayList<>();
         Set<Integer> selectedTabIds =
                 deterministicSetOrder ? new LinkedHashSet<Integer>() : new HashSet<Integer>();
+
         for (TabIdGroup group : tabIdGroups) {
-            List<Tab> groupTabs = new ArrayList<Tab>();
+            List<Tab> groupTabs = new ArrayList<>();
+            List<SavedTabGroupTab> savedTabs = new ArrayList<>();
             for (int tabId : group.getTabIds()) {
                 Tab tab = tabModel.addTab(tabId);
+                tab.setRootId(group.getRootId());
+                tab.setTabGroupId(group.getTabGroupId());
                 if (group.isSelected() && groupTabs.isEmpty()) {
                     selectedTabs.add(tab);
                 }
-                when(filter.isTabInTabGroup(tab)).thenReturn(group.getTabIds().length > 1);
+                when(filter.isTabInTabGroup(tab)).thenReturn(group.getTabGroupId() != null);
                 groupTabs.add(tab);
+
+                SavedTabGroupTab savedTab = new SavedTabGroupTab();
+                savedTab.localId = tabId;
+                savedTabs.add(savedTab);
             }
             if (group.isSelected()) {
                 selectedTabIds.add(group.getTabIdAt(0));
@@ -116,6 +158,16 @@
             when(filter.getRelatedTabList(group.getTabIdAt(0))).thenReturn(groupTabs);
             when(filter.getRelatedTabCountForRootId(group.getTabIdAt(0)))
                     .thenReturn(groupTabs.size());
+
+            if (!group.isGroup() || tabGroupSyncService == null) continue;
+
+            LocalTabGroupId localTabGroupId = new LocalTabGroupId(group.getTabGroupId());
+            SavedTabGroup savedGroup = new SavedTabGroup();
+            savedGroup.localId = localTabGroupId;
+            savedGroup.savedTabs = savedTabs;
+            savedGroup.collaborationId = group.getCollaborationId();
+
+            when(tabGroupSyncService.getGroup(localTabGroupId)).thenReturn(savedGroup);
         }
         when(selectionDelegate.getSelectedItems()).thenReturn(selectedTabIds);
         return new TabListHolder(selectedTabs, selectedAndRelatedTabs);
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorCloseActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorCloseActionUnitTest.java
index e38a5ce8..346890f 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorCloseActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorCloseActionUnitTest.java
@@ -194,14 +194,44 @@
         final boolean actionOnRelatedTabs = true;
         configure(actionOnRelatedTabs);
         List<TabIdGroup> tabIdGroups = new ArrayList<>();
-        tabIdGroups.add(new TabIdGroup(new int[] {0}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {5, 3}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {4}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {8, 7, 6}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {1}, true));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {0},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5, 3},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {4},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {8, 7, 6},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {1},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
         TabListHolder holder =
                 TabListEditorActionUnitTestHelper.configureTabs(
-                        mTabModel, mGroupFilter, mSelectionDelegate, tabIdGroups, true);
+                        mTabModel,
+                        mGroupFilter,
+                        /* tabGroupSyncService= */ null,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        true);
 
         assertEquals(3, holder.getSelectedTabs().size());
         assertEquals(5, holder.getSelectedTabs().get(0).getId());
@@ -240,14 +270,44 @@
         final boolean actionOnRelatedTabs = false;
         configure(actionOnRelatedTabs);
         List<TabIdGroup> tabIdGroups = new ArrayList<>();
-        tabIdGroups.add(new TabIdGroup(new int[] {0}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {5, 3}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {4}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {8, 7, 6}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {1}, true));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {0},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5, 3},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {4},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {8, 7, 6},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {1},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
         TabListHolder holder =
                 TabListEditorActionUnitTestHelper.configureTabs(
-                        mTabModel, mGroupFilter, mSelectionDelegate, tabIdGroups, true);
+                        mTabModel,
+                        mGroupFilter,
+                        /* tabGroupSyncService= */ null,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        true);
 
         assertEquals(3, holder.getSelectedTabs().size());
         assertEquals(5, holder.getSelectedTabs().get(0).getId());
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorGroupActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorGroupActionUnitTest.java
index 5febf51..53d5c35 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorGroupActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorGroupActionUnitTest.java
@@ -4,31 +4,34 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.RuntimeEnvironment;
 
-import org.chromium.base.Token;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
 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.Tab;
+import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_management.TabListEditorAction.ActionDelegate;
 import org.chromium.chrome.browser.tasks.tab_management.TabListEditorAction.ActionObserver;
@@ -40,16 +43,17 @@
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
 
 import java.util.ArrayList;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
 
 /** Unit tests for {@link TabListEditorGroupAction}. */
 @RunWith(BaseRobolectricTestRunner.class)
 public class TabListEditorGroupActionUnitTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
+    @Mock private TabGroupSyncService mTabGroupSyncService;
     @Mock private SelectionDelegate<Integer> mSelectionDelegate;
     @Mock private TabGroupModelFilter mGroupFilter;
     @Mock private ActionDelegate mDelegate;
@@ -60,7 +64,7 @@
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
+        TabGroupSyncServiceFactory.setForTesting(mTabGroupSyncService);
         mAction =
                 TabListEditorGroupAction.createAction(
                         RuntimeEnvironment.application,
@@ -70,55 +74,100 @@
                         IconPosition.START);
         mTabModel = spy(new MockTabModel(mProfile, null));
         when(mGroupFilter.getTabModel()).thenReturn(mTabModel);
-    }
-
-    private void configure(boolean actionOnRelatedTabs) {
-        mAction.configure(() -> mGroupFilter, mSelectionDelegate, mDelegate, actionOnRelatedTabs);
+        mAction.configure(
+                () -> mGroupFilter,
+                mSelectionDelegate,
+                mDelegate,
+                /* editorSupportsActionOnRelatedTabs= */ true);
     }
 
     @Test
     @SmallTest
     public void testInherentActionProperties() {
-        Assert.assertEquals(
+        assertEquals(
                 R.id.tab_list_editor_group_menu_item,
                 mAction.getPropertyModel().get(TabListEditorActionProperties.MENU_ITEM_ID));
-        Assert.assertEquals(
+        assertEquals(
                 R.plurals.tab_selection_editor_group_tabs,
-                mAction.getPropertyModel()
-                        .get(TabListEditorActionProperties.TITLE_RESOURCE_ID));
-        Assert.assertEquals(
+                mAction.getPropertyModel().get(TabListEditorActionProperties.TITLE_RESOURCE_ID));
+        assertEquals(
                 true,
                 mAction.getPropertyModel().get(TabListEditorActionProperties.TITLE_IS_PLURAL));
-        Assert.assertEquals(
+        assertEquals(
                 R.plurals.accessibility_tab_selection_editor_group_tabs,
                 mAction.getPropertyModel()
                         .get(TabListEditorActionProperties.CONTENT_DESCRIPTION_RESOURCE_ID)
                         .intValue());
-        Assert.assertNotNull(mAction.getPropertyModel().get(TabListEditorActionProperties.ICON));
+        assertNotNull(mAction.getPropertyModel().get(TabListEditorActionProperties.ICON));
     }
 
     @Test
     @SmallTest
-    public void testGroupActionDisabled_SingleTabGroupSupport() {
-        configure(false);
+    public void testGroupActionDisabled_NoTabs() {
         List<Integer> tabIds = new ArrayList<>();
         mAction.onSelectionStateChange(tabIds);
-        Assert.assertEquals(
-                false, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
-        Assert.assertEquals(
-                0, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+        assertEquals(false, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(0, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+    }
 
-        int tabId = 1;
-        Tab tab = mTabModel.addTab(tabId);
-        tabIds.add(tabId);
-        tab.setTabGroupId(new Token(1L, 2L));
-        when(mGroupFilter.isTabInTabGroup(tab)).thenReturn(true);
+    @Test
+    @SmallTest
+    public void testGroupActionDisabled_OneTabGroup() {
+        List<TabIdGroup> tabIdGroups = new ArrayList<>();
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {1},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ true));
+        TabListHolder holder =
+                TabListEditorActionUnitTestHelper.configureTabs(
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
 
-        mAction.onSelectionStateChange(tabIds);
-        Assert.assertEquals(
-                false, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
-        Assert.assertEquals(
-                1, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+        mAction.onSelectionStateChange(holder.getSelectedTabIds());
+        assertEquals(false, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(1, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+    }
+
+    @Test
+    @SmallTest
+    public void testGroupActionDisabled_MultipleTabGroupsWithCollaborations() {
+        List<TabIdGroup> tabIdGroups = new ArrayList<>();
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ true));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {3},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ true));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {4},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        TabListHolder holder =
+                TabListEditorActionUnitTestHelper.configureTabs(
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
+
+        mAction.onSelectionStateChange(holder.getSelectedTabIds());
+        assertEquals(false, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(3, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
     }
 
     @Test
@@ -128,33 +177,37 @@
         ChromeFeatureList.TAB_GROUP_CREATION_DIALOG_ANDROID
     })
     public void testSingleTabToGroup() {
-        configure(false);
-        List<Integer> tabIds = new ArrayList<>();
+        List<TabIdGroup> tabIdGroups = new ArrayList<>();
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {1},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        TabListHolder holder =
+                TabListEditorActionUnitTestHelper.configureTabs(
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
 
-        int tabId = 1;
-        Tab tab = mTabModel.addTab(tabId);
-        tabIds.add(tabId);
-        tab.setTabGroupId(null);
-        when(mGroupFilter.isTabInTabGroup(tab)).thenReturn(false);
-        Set<Integer> tabIdsSet = new LinkedHashSet<>(tabIds);
-        when(mSelectionDelegate.getSelectedItems()).thenReturn(tabIdsSet);
+        mAction.onSelectionStateChange(holder.getSelectedTabIds());
+        assertEquals(true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(1, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
 
-        mAction.onSelectionStateChange(tabIds);
-        Assert.assertEquals(
-                true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
-        Assert.assertEquals(
-                1, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
-
-        Assert.assertTrue(mAction.perform());
+        Tab tab = mTabModel.getTabAt(0);
+        assertTrue(mAction.perform());
         verify(mGroupFilter).createSingleTabGroup(tab, true);
         verify(mTabGroupCreationDialogManager).showDialog(tab.getRootId(), mGroupFilter);
 
-        tab.setTabGroupId(new Token(1L, 2L));
         when(mGroupFilter.isTabInTabGroup(tab)).thenReturn(true);
-        Assert.assertTrue(mAction.perform());
+        assertTrue(mAction.perform());
         verify(mGroupFilter, atLeastOnce()).getTabModel();
         verify(mGroupFilter, atLeastOnce()).isTabInTabGroup(any());
-        verifyNoMoreInteractions(mGroupFilter);
+        // Ensure this isn't called again.
+        verify(mGroupFilter).createSingleTabGroup(tab, true);
     }
 
     @Test
@@ -164,55 +217,83 @@
         ChromeFeatureList.TAB_GROUP_CREATION_DIALOG_ANDROID
     })
     public void testGroupActionWithTabs_WillMergingCreateNewGroup() throws Exception {
-        configure(false);
-        List<Integer> tabIds = new ArrayList<>();
-        tabIds.add(5);
-        tabIds.add(3);
-        tabIds.add(7);
-        List<Tab> tabs = new ArrayList<>();
-        for (int id : tabIds) {
-            tabs.add(mTabModel.addTab(id));
-        }
-        Set<Integer> tabIdsSet = new LinkedHashSet<>(tabIds);
-        when(mSelectionDelegate.getSelectedItems()).thenReturn(tabIdsSet);
+        List<TabIdGroup> tabIdGroups = new ArrayList<>();
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {3},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {7},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        TabListHolder holder =
+                TabListEditorActionUnitTestHelper.configureTabs(
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
 
-        List<Tab> tabsToMerge = new ArrayList<>();
-        tabsToMerge.addAll(tabs);
-        tabsToMerge.add(tabs.get(2));
-        when(mGroupFilter.willMergingCreateNewGroup(tabsToMerge)).thenReturn(true);
+        when(mGroupFilter.willMergingCreateNewGroup(any())).thenReturn(true);
 
-        mAction.onSelectionStateChange(tabIds);
-        Assert.assertEquals(
-                true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
-        Assert.assertEquals(
-                3, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+        mAction.onSelectionStateChange(holder.getSelectedTabIds());
+        assertEquals(true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(3, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
 
-        Assert.assertTrue(mAction.perform());
-        verify(mGroupFilter).mergeListOfTabsToGroup(tabs, tabs.get(2), true);
-        verify(mTabGroupCreationDialogManager).showDialog(tabs.get(2).getRootId(), mGroupFilter);
+        List<Tab> selectedTabs = holder.getSelectedTabs();
+        Tab destinationTab = selectedTabs.get(2);
+        assertTrue(mAction.perform());
+        verify(mGroupFilter)
+                .mergeListOfTabsToGroup(selectedTabs.subList(0, 2), destinationTab, true);
+        verify(mTabGroupCreationDialogManager).showDialog(destinationTab.getRootId(), mGroupFilter);
         verify(mDelegate).hideByAction();
     }
 
     @Test
     @SmallTest
     public void testGroupActionWithTabs_MergedIndividualTabsToNewGroup() throws Exception {
-        configure(false);
-        List<Integer> tabIds = new ArrayList<>();
-        tabIds.add(5);
-        tabIds.add(3);
-        tabIds.add(7);
-        List<Tab> tabs = new ArrayList<>();
-        for (int id : tabIds) {
-            tabs.add(mTabModel.addTab(id));
-        }
-        Set<Integer> tabIdsSet = new LinkedHashSet<>(tabIds);
-        when(mSelectionDelegate.getSelectedItems()).thenReturn(tabIdsSet);
+        List<TabIdGroup> tabIdGroups = new ArrayList<>();
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {3},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {7},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        TabListHolder holder =
+                TabListEditorActionUnitTestHelper.configureTabs(
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
 
-        mAction.onSelectionStateChange(tabIds);
-        Assert.assertEquals(
-                true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
-        Assert.assertEquals(
-                3, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+        mAction.onSelectionStateChange(holder.getSelectedTabIds());
+        assertEquals(true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(3, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
 
         final CallbackHelper helper = new CallbackHelper();
         ActionObserver observer =
@@ -224,50 +305,80 @@
                 };
         mAction.addActionObserver(observer);
 
-        Assert.assertTrue(mAction.perform());
-        verify(mGroupFilter).mergeListOfTabsToGroup(tabs, tabs.get(2), true);
+        List<Tab> selectedTabs = holder.getSelectedTabs();
+        Tab destinationTab = selectedTabs.get(2);
+        assertTrue(mAction.perform());
+        verify(mGroupFilter)
+                .mergeListOfTabsToGroup(selectedTabs.subList(0, 2), destinationTab, true);
         verify(mDelegate).hideByAction();
 
         helper.waitForOnly();
         mAction.removeActionObserver(observer);
 
-        Assert.assertTrue(mAction.perform());
-        verify(mGroupFilter, times(2)).mergeListOfTabsToGroup(tabs, tabs.get(2), true);
+        assertTrue(mAction.perform());
+        verify(mGroupFilter, times(2))
+                .mergeListOfTabsToGroup(selectedTabs.subList(0, 2), destinationTab, true);
         verify(mDelegate, times(2)).hideByAction();
-        Assert.assertEquals(1, helper.getCallCount());
+        assertEquals(1, helper.getCallCount());
     }
 
     @Test
     @SmallTest
     public void testGroupActionWithTabGroups_MergeIndividalTabsToExistingGroup() {
-        final boolean actionOnRelatedTabs = true;
-        configure(actionOnRelatedTabs);
         List<TabIdGroup> tabIdGroups = new ArrayList<>();
-        tabIdGroups.add(new TabIdGroup(new int[] {5}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {3}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {4}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {8, 7}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {10, 11, 12}, false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {3},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {4},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {8, 7},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {10, 11, 12},
+                        /* isGroup= */ true,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
         TabListHolder holder =
                 TabListEditorActionUnitTestHelper.configureTabs(
-                        mTabModel, mGroupFilter, mSelectionDelegate, tabIdGroups, false);
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
 
-        Assert.assertEquals(3, holder.getSelectedTabs().size());
-        Assert.assertEquals(5, holder.getSelectedTabs().get(0).getId());
-        Assert.assertEquals(3, holder.getSelectedTabs().get(1).getId());
-        Assert.assertEquals(8, holder.getSelectedTabs().get(2).getId());
+        assertEquals(3, holder.getSelectedTabs().size());
+        assertEquals(5, holder.getSelectedTabs().get(0).getId());
+        assertEquals(3, holder.getSelectedTabs().get(1).getId());
+        assertEquals(8, holder.getSelectedTabs().get(2).getId());
         mAction.onSelectionStateChange(holder.getSelectedTabIds());
-        Assert.assertEquals(
-                true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
-        Assert.assertEquals(
-                4, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+        assertEquals(true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(4, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
 
-        Assert.assertEquals(4, holder.getSelectedAndRelatedTabs().size());
-        Assert.assertEquals(5, holder.getSelectedAndRelatedTabs().get(0).getId());
-        Assert.assertEquals(3, holder.getSelectedAndRelatedTabs().get(1).getId());
-        Assert.assertEquals(8, holder.getSelectedAndRelatedTabs().get(2).getId());
-        Assert.assertEquals(7, holder.getSelectedAndRelatedTabs().get(3).getId());
-        Assert.assertTrue(mAction.perform());
+        assertEquals(4, holder.getSelectedAndRelatedTabs().size());
+        assertEquals(5, holder.getSelectedAndRelatedTabs().get(0).getId());
+        assertEquals(3, holder.getSelectedAndRelatedTabs().get(1).getId());
+        assertEquals(8, holder.getSelectedAndRelatedTabs().get(2).getId());
+        assertEquals(7, holder.getSelectedAndRelatedTabs().get(3).getId());
+        assertTrue(mAction.perform());
         List<Tab> expectedTabs = holder.getSelectedAndRelatedTabs();
         // Remove selected destination tab and all related tabs from the expected tabs list
         List<Tab> destinationAndRelatedTabs =
@@ -281,34 +392,60 @@
     @Test
     @SmallTest
     public void testGroupActionWithTabGroups_MergeGroupToExistingGroup() {
-        final boolean actionOnRelatedTabs = true;
-        configure(actionOnRelatedTabs);
         List<TabIdGroup> tabIdGroups = new ArrayList<>();
-        tabIdGroups.add(new TabIdGroup(new int[] {0}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {5, 3}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {4}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {8, 7, 6}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {10, 11, 12}, false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {0},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5, 3},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {4},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {8, 7, 6},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {10, 11, 12},
+                        /* isGroup= */ true,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
         TabListHolder holder =
                 TabListEditorActionUnitTestHelper.configureTabs(
-                        mTabModel, mGroupFilter, mSelectionDelegate, tabIdGroups, false);
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
 
-        Assert.assertEquals(2, holder.getSelectedTabs().size());
-        Assert.assertEquals(5, holder.getSelectedTabs().get(0).getId());
-        Assert.assertEquals(8, holder.getSelectedTabs().get(1).getId());
+        assertEquals(2, holder.getSelectedTabs().size());
+        assertEquals(5, holder.getSelectedTabs().get(0).getId());
+        assertEquals(8, holder.getSelectedTabs().get(1).getId());
         mAction.onSelectionStateChange(holder.getSelectedTabIds());
-        Assert.assertEquals(
-                true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
-        Assert.assertEquals(
-                5, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+        assertEquals(true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(5, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
 
-        Assert.assertEquals(5, holder.getSelectedAndRelatedTabs().size());
-        Assert.assertEquals(5, holder.getSelectedAndRelatedTabs().get(0).getId());
-        Assert.assertEquals(3, holder.getSelectedAndRelatedTabs().get(1).getId());
-        Assert.assertEquals(8, holder.getSelectedAndRelatedTabs().get(2).getId());
-        Assert.assertEquals(7, holder.getSelectedAndRelatedTabs().get(3).getId());
-        Assert.assertEquals(6, holder.getSelectedAndRelatedTabs().get(4).getId());
-        Assert.assertTrue(mAction.perform());
+        assertEquals(5, holder.getSelectedAndRelatedTabs().size());
+        assertEquals(5, holder.getSelectedAndRelatedTabs().get(0).getId());
+        assertEquals(3, holder.getSelectedAndRelatedTabs().get(1).getId());
+        assertEquals(8, holder.getSelectedAndRelatedTabs().get(2).getId());
+        assertEquals(7, holder.getSelectedAndRelatedTabs().get(3).getId());
+        assertEquals(6, holder.getSelectedAndRelatedTabs().get(4).getId());
+        assertTrue(mAction.perform());
         List<Tab> expectedTabs = holder.getSelectedAndRelatedTabs();
         // Remove selected destination tab and all related tabs from the expected tabs list
         List<Tab> destinationAndRelatedTabs =
@@ -322,41 +459,72 @@
     @Test
     @SmallTest
     public void testGroupActionWithTabGroups_MergeTabsAndGroupsToExistingGroup() {
-        final boolean actionOnRelatedTabs = true;
-        configure(actionOnRelatedTabs);
         List<TabIdGroup> tabIdGroups = new ArrayList<>();
-        tabIdGroups.add(new TabIdGroup(new int[] {0}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {5, 3}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {4}, false));
-        tabIdGroups.add(new TabIdGroup(new int[] {8, 7, 6}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {10, 11, 12}, true));
-        tabIdGroups.add(new TabIdGroup(new int[] {1}, true));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {0},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5, 3},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {4},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {8, 7, 6},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {10, 11, 12},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {1},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
         TabListHolder holder =
                 TabListEditorActionUnitTestHelper.configureTabs(
-                        mTabModel, mGroupFilter, mSelectionDelegate, tabIdGroups, false);
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
 
-        Assert.assertEquals(4, holder.getSelectedTabs().size());
-        Assert.assertEquals(5, holder.getSelectedTabs().get(0).getId());
-        Assert.assertEquals(8, holder.getSelectedTabs().get(1).getId());
-        Assert.assertEquals(10, holder.getSelectedTabs().get(2).getId());
-        Assert.assertEquals(1, holder.getSelectedTabs().get(3).getId());
+        assertEquals(4, holder.getSelectedTabs().size());
+        assertEquals(5, holder.getSelectedTabs().get(0).getId());
+        assertEquals(8, holder.getSelectedTabs().get(1).getId());
+        assertEquals(10, holder.getSelectedTabs().get(2).getId());
+        assertEquals(1, holder.getSelectedTabs().get(3).getId());
         mAction.onSelectionStateChange(holder.getSelectedTabIds());
-        Assert.assertEquals(
-                true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
-        Assert.assertEquals(
-                9, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+        assertEquals(true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(9, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
 
-        Assert.assertEquals(9, holder.getSelectedAndRelatedTabs().size());
-        Assert.assertEquals(5, holder.getSelectedAndRelatedTabs().get(0).getId());
-        Assert.assertEquals(3, holder.getSelectedAndRelatedTabs().get(1).getId());
-        Assert.assertEquals(8, holder.getSelectedAndRelatedTabs().get(2).getId());
-        Assert.assertEquals(7, holder.getSelectedAndRelatedTabs().get(3).getId());
-        Assert.assertEquals(6, holder.getSelectedAndRelatedTabs().get(4).getId());
-        Assert.assertEquals(10, holder.getSelectedAndRelatedTabs().get(5).getId());
-        Assert.assertEquals(11, holder.getSelectedAndRelatedTabs().get(6).getId());
-        Assert.assertEquals(12, holder.getSelectedAndRelatedTabs().get(7).getId());
-        Assert.assertEquals(1, holder.getSelectedAndRelatedTabs().get(8).getId());
-        Assert.assertTrue(mAction.perform());
+        assertEquals(9, holder.getSelectedAndRelatedTabs().size());
+        assertEquals(5, holder.getSelectedAndRelatedTabs().get(0).getId());
+        assertEquals(3, holder.getSelectedAndRelatedTabs().get(1).getId());
+        assertEquals(8, holder.getSelectedAndRelatedTabs().get(2).getId());
+        assertEquals(7, holder.getSelectedAndRelatedTabs().get(3).getId());
+        assertEquals(6, holder.getSelectedAndRelatedTabs().get(4).getId());
+        assertEquals(10, holder.getSelectedAndRelatedTabs().get(5).getId());
+        assertEquals(11, holder.getSelectedAndRelatedTabs().get(6).getId());
+        assertEquals(12, holder.getSelectedAndRelatedTabs().get(7).getId());
+        assertEquals(1, holder.getSelectedAndRelatedTabs().get(8).getId());
+        assertTrue(mAction.perform());
         List<Tab> expectedTabs = holder.getSelectedAndRelatedTabs();
         // Remove selected destination tab and all related tabs from the expected tabs list
         List<Tab> destinationAndRelatedTabs =
@@ -366,4 +534,161 @@
                 .mergeListOfTabsToGroup(expectedTabs, holder.getSelectedTabs().get(0), true);
         verify(mDelegate).hideByAction();
     }
+
+    @Test
+    @SmallTest
+    public void testGroupActionWithTabGroups_MergeTabsAndGroupsToCollaborationGroup() {
+        List<TabIdGroup> tabIdGroups = new ArrayList<>();
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {0},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5, 3},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {4},
+                        /* isGroup= */ false,
+                        /* selected= */ false,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {8, 7, 6},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ true));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {10, 11, 12},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {1},
+                        /* isGroup= */ false,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        TabListHolder holder =
+                TabListEditorActionUnitTestHelper.configureTabs(
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
+
+        List<Tab> selectedTabs = holder.getSelectedTabs();
+        assertEquals(4, selectedTabs.size());
+        assertEquals(5, selectedTabs.get(0).getId());
+        assertEquals(8, selectedTabs.get(1).getId());
+        assertEquals(10, selectedTabs.get(2).getId());
+        assertEquals(1, selectedTabs.get(3).getId());
+        mAction.onSelectionStateChange(holder.getSelectedTabIds());
+        assertEquals(true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(9, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+
+        List<Tab> selectedAndRelatedTabs = holder.getSelectedAndRelatedTabs();
+        assertEquals(9, selectedAndRelatedTabs.size());
+        assertEquals(5, selectedAndRelatedTabs.get(0).getId());
+        assertEquals(3, selectedAndRelatedTabs.get(1).getId());
+        assertEquals(8, selectedAndRelatedTabs.get(2).getId());
+        assertEquals(7, selectedAndRelatedTabs.get(3).getId());
+        assertEquals(6, selectedAndRelatedTabs.get(4).getId());
+        assertEquals(10, selectedAndRelatedTabs.get(5).getId());
+        assertEquals(11, selectedAndRelatedTabs.get(6).getId());
+        assertEquals(12, selectedAndRelatedTabs.get(7).getId());
+        assertEquals(1, selectedAndRelatedTabs.get(8).getId());
+        assertTrue(mAction.perform());
+
+        List<Tab> expectedTabs = selectedAndRelatedTabs;
+        // Remove selected destination tab and all related tabs from the expected tabs list. Note
+        // that we are merging to the collaboration group.
+        List<Tab> destinationAndRelatedTabs =
+                mGroupFilter.getRelatedTabList(holder.getSelectedTabIds().get(1));
+        expectedTabs.removeAll(destinationAndRelatedTabs);
+        verify(mGroupFilter)
+                .mergeListOfTabsToGroup(expectedTabs, holder.getSelectedTabs().get(1), true);
+        verify(mDelegate).hideByAction();
+    }
+
+    @Test
+    @SmallTest
+    public void testGroupActionWithTabGroups_MergeCollaborationIsFirst() {
+        List<TabIdGroup> tabIdGroups = new ArrayList<>();
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5, 3},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ true));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {8, 7, 6},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ false));
+        TabListHolder holder =
+                TabListEditorActionUnitTestHelper.configureTabs(
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
+
+        mAction.onSelectionStateChange(holder.getSelectedTabIds());
+        assertEquals(true, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(5, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+
+        assertTrue(mAction.perform());
+
+        List<Tab> expectedTabs = holder.getSelectedAndRelatedTabs();
+        // Remove selected destination tab and all related tabs from the expected tabs list. Note
+        // that we are merging to the collaboration group.
+        List<Tab> destinationAndRelatedTabs =
+                mGroupFilter.getRelatedTabList(holder.getSelectedTabIds().get(0));
+        expectedTabs.removeAll(destinationAndRelatedTabs);
+        verify(mGroupFilter)
+                .mergeListOfTabsToGroup(expectedTabs, holder.getSelectedTabs().get(0), true);
+        verify(mDelegate).hideByAction();
+    }
+
+    @Test(expected = AssertionError.class)
+    @SmallTest
+    public void testGroupActionWithTabGroups_MergeCollaborationsAsserts() {
+        List<TabIdGroup> tabIdGroups = new ArrayList<>();
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {5, 3},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ true));
+        tabIdGroups.add(
+                new TabIdGroup(
+                        new int[] {8, 7, 6},
+                        /* isGroup= */ true,
+                        /* selected= */ true,
+                        /* isCollaboration= */ true));
+        TabListHolder holder =
+                TabListEditorActionUnitTestHelper.configureTabs(
+                        mTabModel,
+                        mGroupFilter,
+                        mTabGroupSyncService,
+                        mSelectionDelegate,
+                        tabIdGroups,
+                        false);
+
+        mAction.onSelectionStateChange(holder.getSelectedTabIds());
+        assertEquals(false, mAction.getPropertyModel().get(TabListEditorActionProperties.ENABLED));
+        assertEquals(5, mAction.getPropertyModel().get(TabListEditorActionProperties.ITEM_COUNT));
+
+        mAction.perform();
+    }
 }
diff --git a/chrome/android/java/res/layout/tab_strip_group_menu_layout.xml b/chrome/android/java/res/layout/tab_strip_group_menu_layout.xml
index 109d1bf..8ea7c7e 100644
--- a/chrome/android/java/res/layout/tab_strip_group_menu_layout.xml
+++ b/chrome/android/java/res/layout/tab_strip_group_menu_layout.xml
@@ -5,38 +5,42 @@
 found in the LICENSE file.
 -->
 
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-  <EditText
-      tools:ignore="Autofill,LabelFor"
-      android:id="@+id/tab_group_title"
-      android:layout_height="0dp"
-      android:layout_width="match_parent"
-      android:layout_weight="1"
-      android:layout_marginTop="12dp"
-      android:layout_marginBottom="8dp"
-      android:layout_marginStart="8dp"
-      android:layout_marginEnd="8dp"
-      android:singleLine="true"
-      android:ellipsize="end"
-      android:textAppearance="@style/TextAppearance.TextLarge.Primary"
-      android:inputType="text|textNoSuggestions"
-      android:imeOptions="actionDone"
-      android:theme="@style/TabStripGroupContextMenuTitleTheme"
-      android:selectAllOnFocus="true"
-      android:hint="@string/title"/>
-
-  <ViewStub
-      android:id="@+id/color_picker_stub"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+  <LinearLayout
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
-      android:layout="@layout/tab_group_color_picker_container" />
+      android:orientation="vertical">
 
-  <include
-      layout="@layout/tab_switcher_action_menu_layout" />
-</LinearLayout>
+    <EditText
+        tools:ignore="Autofill,LabelFor"
+        android:id="@+id/tab_group_title"
+        android:layout_height="0dp"
+        android:layout_width="match_parent"
+        android:layout_weight="1"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="8dp"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="8dp"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:textAppearance="@style/TextAppearance.TextLarge.Primary"
+        android:inputType="text|textNoSuggestions"
+        android:imeOptions="actionDone"
+        android:theme="@style/TabStripGroupContextMenuTitleTheme"
+        android:selectAllOnFocus="true"
+        android:hint="@string/title"/>
+
+    <ViewStub
+        android:id="@+id/color_picker_stub"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout="@layout/tab_group_color_picker_container" />
+
+    <include
+        layout="@layout/tab_switcher_action_menu_layout" />
+  </LinearLayout>
+</ScrollView>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 08920909..44d8077 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -7,6 +7,7 @@
 import static org.chromium.chrome.browser.ui.IncognitoRestoreAppLaunchDrawBlocker.IS_INCOGNITO_SELECTED;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ShortcutManager;
 import android.content.res.Configuration;
@@ -29,6 +30,7 @@
 import android.view.WindowManager;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
@@ -103,6 +105,7 @@
 import org.chromium.chrome.browser.download.DownloadOpenSource;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.dragdrop.ChromeDragAndDropBrowserDelegate;
+import org.chromium.chrome.browser.educational_tip.EducationTipModuleActionDelegate;
 import org.chromium.chrome.browser.educational_tip.EducationalTipModuleBuilder;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.feed.FeedSurfaceTracker;
@@ -191,6 +194,7 @@
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tab.tab_restore.HistoricalTabModelObserver;
 import org.chromium.chrome.browser.tab_resumption.TabResumptionModuleBuilder;
+import org.chromium.chrome.browser.tab_ui.TabGridIphDialogCoordinator;
 import org.chromium.chrome.browser.tab_ui.TabSwitcher;
 import org.chromium.chrome.browser.tab_ui.TabSwitcherUtils;
 import org.chromium.chrome.browser.tabbed_mode.TabbedAppMenuPropertiesDelegate;
@@ -233,6 +237,7 @@
 import org.chromium.chrome.browser.undo_tab_close_snackbar.UndoBarController;
 import org.chromium.chrome.browser.usage_stats.UsageStatsService;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate;
 import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate;
@@ -2282,25 +2287,58 @@
 
         if (ChromeFeatureList.sEducationalTipModule.isEnabled()) {
             EducationalTipModuleBuilder educationalTipModuleBuilder =
-                    new EducationalTipModuleBuilder(
-                            this,
-                            mRootUiCoordinator.getBottomSheetController(),
-                            getModalDialogManagerSupplier(),
-                            (paneId) -> {
-                                if (mLayoutManager != null) {
-                                    HubShowPaneHelper hubShowPaneHelper =
-                                            mHubProvider.getHubShowPaneHelper();
-                                    hubShowPaneHelper.setPaneToShow(paneId);
-                                    mLayoutManager.showLayout(LayoutType.TAB_SWITCHER, false);
-                                }
-                            },
-                            () -> mCompositorViewHolder);
+                    new EducationalTipModuleBuilder(createEducationTipModuleActionDelegate());
             moduleRegistry.registerModule(ModuleType.EDUCATIONAL_TIP, educationalTipModuleBuilder);
         }
 
         mModuleRegistrySupplier.set(moduleRegistry);
     }
 
+    private EducationTipModuleActionDelegate createEducationTipModuleActionDelegate() {
+        return new EducationTipModuleActionDelegate() {
+            @NonNull
+            @Override
+            public Context getContext() {
+                return ChromeTabbedActivity.this;
+            }
+
+            @NonNull
+            @Override
+            public BottomSheetController getBottomSheetController() {
+                return mRootUiCoordinator.getBottomSheetController();
+            }
+
+            @Override
+            public void openHubPane(int paneId) {
+                if (mLayoutManager == null) return;
+
+                // Opens the tab switcher and displays a specific pane.
+                HubShowPaneHelper hubShowPaneHelper = mHubProvider.getHubShowPaneHelper();
+                hubShowPaneHelper.setPaneToShow(paneId);
+                mLayoutManager.showLayout(LayoutType.TAB_SWITCHER, false);
+            }
+
+            @Override
+            public void openTabGroupIphDialog() {
+                TabGridIphDialogCoordinator tabGridIphDialogCoordinator =
+                        new TabGridIphDialogCoordinator(
+                                ChromeTabbedActivity.this, getModalDialogManager());
+                tabGridIphDialogCoordinator.setParentView(mCompositorViewHolder);
+
+                mLayoutManager.showLayout(LayoutType.TAB_SWITCHER, false);
+                tabGridIphDialogCoordinator.showIph();
+            }
+
+            @Override
+            public void openAndHighlightQuickDeleteMenuItem() {
+                // Opens the app menu and highlights the quick delete menu item.
+                mRootUiCoordinator.getAppMenuHandler().setMenuHighlight(R.id.quick_delete_menu_id);
+                getMenuOrKeyboardActionController()
+                        .onMenuOrKeyboardAction(R.id.show_menu, /* fromMenu= */ false);
+            }
+        };
+    }
+
     private boolean shouldIgnoreIntent() {
         if (mShouldIgnoreIntent == null) {
             // We call this only once to give a consistent view of whether the intent should be
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index 1e1e6ac..38cbfa7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -33,6 +33,7 @@
 import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils.ErrorType;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
@@ -193,7 +194,7 @@
                 cardExpiration,
                 cardArtUrl,
                 cardIconId,
-                AutofillUiUtils.CardIconSize.LARGE,
+                ImageSize.LARGE,
                 R.dimen.card_unmask_dialog_credit_card_icon_end_margin,
                 /* cardNameAndNumberTextAppearance= */ R.style.TextAppearance_TextLarge_Primary,
                 /* cardLabelTextAppearance= */ R.style.TextAppearance_TextMedium_Secondary,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePrompt.java
index 4d088c53..f43fb880 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePrompt.java
@@ -12,14 +12,11 @@
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.EditText;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.google.android.material.textfield.TextInputLayout;
-
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
 
@@ -28,10 +25,8 @@
 import org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator;
 import org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.Delegate;
 import org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.UserFlow;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.autofill.AutofillProfile;
-import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -77,8 +72,6 @@
             userFlow = SAVE_NEW_ADDRESS_PROFILE;
         }
 
-        if (!isUpdate && !isMigrationToAccount) setupAddressNickname();
-
         PropertyModel.Builder builder =
                 new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                         .with(
@@ -270,30 +263,6 @@
                 .setVisibility(show ? View.GONE : View.VISIBLE);
     }
 
-    private void setupAddressNickname() {
-        TextInputLayout nicknameInputLayout = mDialogView.findViewById(R.id.nickname_input_layout);
-        if (!ChromeFeatureList.isEnabled(
-                ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT)) {
-            nicknameInputLayout.setVisibility(View.GONE);
-            return;
-        }
-        EditText nicknameInput = mDialogView.findViewById(R.id.nickname_input);
-        nicknameInput.setOnFocusChangeListener(
-                (v, hasFocus) ->
-                        nicknameInputLayout.setHint(
-                                !hasFocus && TextUtils.isEmpty(nicknameInput.getText())
-                                        // TODO(crbug.com/40267973): Use localized strings.
-                                        ? "Add a label"
-                                        : "Label"));
-
-        // Prevent input from being focused when keyboard is closed.
-        KeyboardVisibilityDelegate.getInstance()
-                .addKeyboardVisibilityListener(
-                        isShowing -> {
-                            if (!isShowing && nicknameInput.hasFocus()) nicknameInput.clearFocus();
-                        });
-    }
-
     void setAddressEditorForTesting(AddressEditorCoordinator addressEditor) {
         var oldValue = mAddressEditor;
         mAddressEditor = addressEditor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
index 458f21d..2fe4cf7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
@@ -33,7 +33,6 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.AutofillEditorBase;
-import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.Iban;
@@ -45,6 +44,7 @@
 import org.chromium.chrome.browser.settings.ChromeBaseSettingsFragment;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
 import org.chromium.chrome.browser.settings.SettingsLauncherFactory;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.MandatoryReauthAuthenticationFlowEvent;
 import org.chromium.components.autofill.VirtualCardEnrollmentState;
 import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
@@ -282,7 +282,7 @@
                             personalDataManager,
                             card.getCardArtUrl(),
                             card.getIssuerIconDrawableId(),
-                            AutofillUiUtils.CardIconSize.LARGE,
+                            ImageSize.LARGE,
                             ChromeFeatureList.isEnabled(
                                     ChromeFeatureList.AUTOFILL_ENABLE_CARD_ART_IMAGE)));
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditor.java
index 6cfb112..94b5345 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditor.java
@@ -24,12 +24,12 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.build.annotations.UsedByReflection;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.PersonalDataManagerFactory;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.components.autofill.AutofillProfile;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.VirtualCardEnrollmentLinkType;
 import org.chromium.components.autofill.VirtualCardEnrollmentState;
 import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
@@ -163,7 +163,7 @@
                         PersonalDataManagerFactory.getForProfile(getProfile()),
                         mCard.getCardArtUrl(),
                         mCard.getIssuerIconDrawableId(),
-                        AutofillUiUtils.CardIconSize.LARGE,
+                        ImageSize.LARGE,
                         ChromeFeatureList.isEnabled(
                                 ChromeFeatureList.AUTOFILL_ENABLE_CARD_ART_IMAGE)));
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialog.java
index db94a85..164f90b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialog.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.ChromeStringConstants;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.VirtualCardEnrollmentLinkType;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
@@ -138,7 +139,7 @@
                         R.string.autofill_virtual_card_enrollment_dialog_card_container_title),
                 mVirtualCardEnrollmentFields.getCardArtUrl(),
                 mVirtualCardEnrollmentFields.getNetworkIconId(),
-                AutofillUiUtils.CardIconSize.LARGE,
+                ImageSize.LARGE,
                 R.dimen.virtual_card_enrollment_dialog_card_container_issuer_icon_margin_end,
                 /* cardNameAndNumberTextAppearance= */ R.style.TextAppearance_TextLarge_Primary,
                 /* cardLabelTextAppearance= */ R.style.TextAppearance_TextMedium_Secondary,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragment.java
index ef9cc97..b2078b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragment.java
@@ -26,6 +26,7 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager.PersonalDataManagerObserver;
 import org.chromium.chrome.browser.autofill.PersonalDataManagerFactory;
 import org.chromium.chrome.browser.settings.ChromeBaseSettingsFragment;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.payments.AccountType;
 import org.chromium.components.autofill.payments.BankAccount;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
@@ -170,7 +171,7 @@
                     mPersonalDataManager.getCustomImageForAutofillSuggestionIfAvailable(
                             bankAccount.getDisplayIconUrl(),
                             AutofillUiUtils.CardIconSpecs.create(
-                                    getStyledContext(), AutofillUiUtils.CardIconSize.LARGE));
+                                    getStyledContext(), ImageSize.LARGE));
         }
         Drawable displayIconBitmapDrawable =
                 displayIconOptional.isPresent()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetBridge.java
index 754f7a26..516ad716 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetBridge.java
@@ -26,6 +26,7 @@
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.VirtualCardEnrollmentLinkType;
 import org.chromium.components.autofill.payments.LegalMessageLine;
 import org.chromium.content_public.browser.WebContents;
@@ -103,7 +104,7 @@
         mNativeAutofillVcnEnrollBottomSheetBridge = nativeAutofillVcnEnrollBottomSheetBridge;
 
         AutofillUiUtils.CardIconSpecs cardIconSpecs =
-                AutofillUiUtils.CardIconSpecs.create(mContext, AutofillUiUtils.CardIconSize.LARGE);
+                AutofillUiUtils.CardIconSpecs.create(mContext, ImageSize.LARGE);
 
         PropertyModel.Builder modelBuilder =
                 new PropertyModel.Builder(AutofillVcnEnrollBottomSheetProperties.ALL_KEYS)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
index 6ba868de35c..1ce40871 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -8,8 +8,12 @@
 import android.text.Editable;
 import android.text.TextUtils;
 import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.EditText;
+import android.widget.ListAdapter;
+import android.widget.ListView;
 
 import androidx.annotation.DimenRes;
 import androidx.annotation.VisibleForTesting;
@@ -226,6 +230,35 @@
                             isIncognito,
                             true));
         }
+
+        setListViewHeightBasedOnChildren();
+    }
+
+    /**
+     * Calculates and sets the total height of the action menu ListView to prevent the ListView from
+     * collapsing when nested inside a parent ScrollView.
+     */
+    public void setListViewHeightBasedOnChildren() {
+        assert mContentView != null : "Menu view should not be null";
+
+        ListView listView = mContentView.findViewById(R.id.tab_group_action_menu_list);
+        listView.setScrollContainer(false);
+
+        ListAdapter listAdapter = listView.getAdapter();
+        if (listAdapter == null) {
+            return;
+        }
+
+        int totalHeight = 0;
+        for (int i = 0; i < listAdapter.getCount(); i++) {
+            View listItem = listAdapter.getView(i, null, listView);
+            listItem.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            totalHeight += listItem.getMeasuredHeight();
+        }
+
+        ViewGroup.LayoutParams params = listView.getLayoutParams();
+        params.height = totalHeight + listView.getPaddingTop() + listView.getPaddingBottom();
+        listView.setLayoutParams(params);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java
index a1529581..42cf8a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java
@@ -53,13 +53,19 @@
 
     // This should be kept in sync with the definition |CustomTabsCloseCause|
     // in tools/metrics/histograms/enums.xml.
-    @IntDef({CloseCause.USER_ACTION_CHROME, CloseCause.USER_ACTION_ANDROID, CloseCause.AUTOCLOSE})
+    @IntDef({
+        CloseCause.USER_ACTION_CHROME,
+        CloseCause.USER_ACTION_ANDROID,
+        CloseCause.AUTOCLOSE,
+        CloseCause.AUTH_TAB
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CloseCause {
         int USER_ACTION_CHROME = 0;
         int USER_ACTION_ANDROID = 1;
         int AUTOCLOSE = 2;
-        int COUNT = 3;
+        int AUTH_TAB = 3;
+        int COUNT = 4;
     }
 
     private @CloseCause int mCloseCause;
@@ -85,6 +91,9 @@
     @Override
     public void onStopWithNative() {
         assert mOnStartTimestampMs != 0;
+        if (mCloseCause == CloseCause.AUTOCLOSE && mIntent.isAuthTab()) {
+            mCloseCause = CloseCause.AUTH_TAB;
+        }
         RecordHistogram.recordEnumeratedHistogram(
                 "CustomTabs.CloseCause", mCloseCause, CloseCause.COUNT);
 
@@ -98,7 +107,8 @@
 
         if (mIsCctFinishing.getAsBoolean()) {
             long time = System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS;
-            boolean wasUserClose = mCloseCause != CloseCause.AUTOCLOSE;
+            boolean wasUserClose =
+                    mCloseCause != CloseCause.AUTOCLOSE && mCloseCause != CloseCause.AUTH_TAB;
             boolean isPartial = mIntent.isPartialCustomTab();
 
             long recordDuration = Math.min(duration / DateUtils.SECOND_IN_MILLIS, 300);
@@ -149,7 +159,7 @@
                 long time,
                 String packageName,
                 long sessionDuration,
-                boolean wasAutomaticallyClosed,
+                boolean wasUserClosed,
                 boolean isPartialCct);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
index 52d5498..de58065e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
@@ -161,13 +161,7 @@
         // The absolute offset has to take into account that the circle starts at an offset
         mTotalDragDistance = RAW_SWIPE_LIMIT_DP * getResources().getDisplayMetrics().density;
 
-        mAnimateToStartPosition.setAnimationListener(
-                new EmptyAnimationListener() {
-                    @Override
-                    public void onAnimationEnd(Animation animation) {
-                        reset();
-                    }
-                });
+        prepareAnimateToStartPosition();
     }
 
     /** Set the listener to be notified when the navigation is triggered. */
@@ -274,24 +268,36 @@
     }
 
     /**
-     * Start the pull effect. If the effect is disabled or a navigation animation
-     * is currently active, the request will be ignored.
+     * Start the pull effect. If the effect is disabled or a navigation animation is currently
+     * active, the request will be ignored.
+     *
      * @return whether a new pull sequence has started.
      */
     public boolean start() {
         if (!isEnabled() || mNavigating || mListener == null) return false;
+
+        // Stop animation triggered by previous slide.
+        if (mAnimateToStartPosition.hasStarted()) {
+            mAnimateToStartPosition.setAnimationListener(null);
+            mArrowView.clearAnimation();
+            mAnimateToStartPosition.cancel();
+            mAnimateToStartPosition.reset();
+        }
+
         mTotalMotion = 0;
         mMaxOverscroll = 0.f;
         mIsBeingDragged = true;
         mWillNavigate = false;
         initializeOffset();
+        prepareAnimateToStartPosition();
         mArrowView.setFaded(false, false);
         return true;
     }
 
     /**
-     * Apply a pull impulse to the effect. If the effect is disabled or has yet
-     * to start, the pull will be ignored.
+     * Apply a pull impulse to the effect. If the effect is disabled or has yet to start, the pull
+     * will be ignored.
+     *
      * @param offset Updated total pull offset.
      */
     public void pull(float offset) {
@@ -363,11 +369,22 @@
         mCurrentTargetOffset = mArrowView.getLeft();
     }
 
+    private void prepareAnimateToStartPosition() {
+        mAnimateToStartPosition.setAnimationListener(
+                new EmptyAnimationListener() {
+                    @Override
+                    public void onAnimationEnd(Animation animation) {
+                        reset();
+                    }
+                });
+    }
+
     /**
-     * Release the active pull. If no pull has started, the release will be ignored.
-     * If the pull was sufficiently large, the navigation sequence will be initiated.
-     * @param allowNav whether to allow a sufficiently large pull to trigger
-     *                     the navigation action and animation sequence.
+     * Release the active pull. If no pull has started, the release will be ignored. If the pull was
+     * sufficiently large, the navigation sequence will be initiated.
+     *
+     * @param allowNav whether to allow a sufficiently large pull to trigger the navigation action
+     *     and animation sequence.
      */
     public void release(boolean allowNav) {
         if (!mIsBeingDragged) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillVirtualCardEnrollmentInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillVirtualCardEnrollmentInfoBar.java
index b09f244..dd8d3d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillVirtualCardEnrollmentInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AutofillVirtualCardEnrollmentInfoBar.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeStringConstants;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.VirtualCardEnrollmentLinkType;
 import org.chromium.components.autofill.payments.LegalMessageLine;
 import org.chromium.components.infobars.ConfirmInfoBar;
@@ -253,8 +254,7 @@
 
         // Get and resize the issuer icon.
         AutofillUiUtils.CardIconSpecs cardIconSpecs =
-                AutofillUiUtils.CardIconSpecs.create(
-                        layout.getContext(), AutofillUiUtils.CardIconSize.LARGE);
+                AutofillUiUtils.CardIconSpecs.create(layout.getContext(), ImageSize.LARGE);
         Bitmap scaledIssuerIcon =
                 Bitmap.createScaledBitmap(
                         mIssuerIcon, cardIconSpecs.getWidth(), cardIconSpecs.getHeight(), true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
index f06acbe3..edc7623 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -66,9 +66,19 @@
  * The Chrome settings activity.
  *
  * <p>This activity displays a single {@link Fragment}, typically a {@link
- * PreferenceFragmentCompat}. As the user navigates through settings, a separate Settings activity
- * is created for each screen. Thus each fragment may freely modify its activity's action bar or
- * title. This mimics the behavior of {@link android.preference.PreferenceActivity}.
+ * PreferenceFragmentCompat}. There are two types of fragments shown in the activity:
+ * <i>embeddable</i> fragments that implement {@link SettingsPage}, and <i>standalone</i> fragments
+ * that do not implement it. Embeddable fragments may be embedded into a column in the multi-column
+ * settings UI, if it is enabled and the window is large enough. Standalone fragments, in contrast,
+ * are always shown as occupying the whole window.
+ *
+ * <p>Embeddable fragments must not modify the activity UI outside of the fragment, e.g. the
+ * activity title and the action bar, because the same activity instance is shared among multiple
+ * fragments as the user navigates through the settings. Instead, fragments should implement methods
+ * in {@link SettingsPage} to ask the activity to update its UI appropriately.
+ *
+ * <p>Standalone fragments may modify the activity UI as needed. A standalone fragment is always
+ * launched with a fresh settings activity instance that is not shared with other fragments.
  */
 public class SettingsActivity extends ChromeBaseAppCompatActivity
         implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, SnackbarManageable {
@@ -76,6 +86,7 @@
     public static final String EXTRA_SHOW_FRAGMENT = "show_fragment";
 
     static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = "show_fragment_args";
+    static final String EXTRA_SHOW_FRAGMENT_STANDALONE = "show_fragment_standalone";
 
     /** The current instance of SettingsActivity in the resumed state, if any. */
     private static SettingsActivity sResumedInstance;
@@ -85,8 +96,8 @@
 
     private static boolean sActivityNotExportedChecked;
 
+    private boolean mStandalone;
     private Profile mProfile;
-
     private ScrimCoordinator mScrim;
     private ManagedBottomSheetController mManagedBottomSheetController;
     private final OneshotSupplierImpl<BottomSheetController> mBottomSheetControllerSupplier =
@@ -103,6 +114,8 @@
     @SuppressLint("InlinedApi")
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        mStandalone = getIntent().getBooleanExtra(EXTRA_SHOW_FRAGMENT_STANDALONE, false);
+
         setTitle(R.string.settings);
         ensureActivityNotExported();
 
@@ -125,11 +138,13 @@
                         getModalDialogManagerSupplier()),
                 true /* recursive */);
         fragmentManager.registerFragmentLifecycleCallbacks(
-                new TitleUpdater(), false /* recursive */);
-        fragmentManager.registerFragmentLifecycleCallbacks(
                 new WideDisplayPaddingApplier(), false /* recursive */);
         fragmentManager.registerFragmentLifecycleCallbacks(
                 new SettingsMetricsReporter(), false /* recursive */);
+        if (!mStandalone) {
+            fragmentManager.registerFragmentLifecycleCallbacks(
+                    new TitleUpdater(), false /* recursive */);
+        }
 
         super.onCreate(savedInstanceState);
 
@@ -166,6 +181,17 @@
         // mode.
         assert ChromeFeatureList.sSettingsSingleActivity.isEnabled();
 
+        if (mStandalone) {
+            // A standalone activity attempted to launch a non-standalone activity, but the intent
+            // was delivered to the standalone activity itself because of FLAG_ACTIVITY_SINGLE_TOP.
+            // Resend the intent without the flag to start a new activity. Bouncing activities has
+            // some cost in terms of time to launch the final activity, but this is fairly a rare
+            // flow anyway.
+            intent.removeFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            startActivity(intent);
+            return;
+        }
+
         Fragment fragment = instantiateMainFragment(intent);
         // TODO(b/356743945): Enable transition.
         getSupportFragmentManager()
@@ -450,13 +476,8 @@
                 return;
             }
 
-            // TODO(b/356743945): Enforce that all main fragments implement SettingsPage.
-            // For now, PrivacyGuideFragment is shown with SettingsActivity but it does not
-            // implement
-            // SettingsPage.
-            if (!(fragment instanceof SettingsPage settingsFragment)) {
-                return;
-            }
+            // TitleUpdater is enabled only when the fragment implements SettingsPage.
+            SettingsPage settingsFragment = (SettingsPage) fragment;
 
             if (mCurrentPageTitle != null) {
                 mCurrentPageTitle.removeObserver(mSetTitleCallback);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsIntentUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsIntentUtil.java
index fe30e1a..20ef15d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsIntentUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsIntentUtil.java
@@ -13,6 +13,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.components.browser_ui.settings.SettingsPage;
 
 public class SettingsIntentUtil {
     private SettingsIntentUtil() {}
@@ -32,7 +33,13 @@
             @Nullable Bundle fragmentArgs) {
         Intent intent = new Intent();
         intent.setClass(context, SettingsActivity.class);
-        if (ChromeFeatureList.sSettingsSingleActivity.isEnabled()) {
+        if (isStandaloneFragment(context, fragmentName)) {
+            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_STANDALONE, true);
+        } else if (ChromeFeatureList.sSettingsSingleActivity.isEnabled()) {
+            // Note that this intent will be delivered to an existing settings activity (if it
+            // exists) even if it is hosting a standalone fragment. In this case, the activity will
+            // resend the intent without the flag to start a new activity. See
+            // SettingsActivity#onNewIntent.
             intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         }
         if (!(context instanceof Activity)) {
@@ -47,4 +54,27 @@
         }
         return intent;
     }
+
+    /**
+     * Checks if a given fragment is a standalone fragment.
+     *
+     * <p>A fragment is standalone if it does not implement {@link SettingsPage}. Such fragments are
+     * shown in separate activities and have full control over the whole UI. See {@link
+     * SettingsActivity} for details.
+     */
+    private static boolean isStandaloneFragment(
+            @NonNull Context context, @Nullable String fragmentName) {
+        if (fragmentName == null) {
+            return false;
+        }
+
+        Class<?> fragmentClass;
+        try {
+            fragmentClass = context.getClassLoader().loadClass(fragmentName);
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        return !SettingsPage.class.isAssignableFrom(fragmentClass);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 409b133..2fe6e97c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -25,7 +25,6 @@
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
-import org.chromium.base.supplier.OneShotCallback;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.supplier.Supplier;
@@ -117,7 +116,6 @@
 import org.chromium.chrome.browser.tab_ui.TabSwitcher;
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_management.UndoGroupSnackbarController;
 import org.chromium.chrome.browser.toolbar.ToolbarButtonInProductHelpController;
 import org.chromium.chrome.browser.toolbar.ToolbarFeatures;
@@ -200,8 +198,6 @@
     private final ManualFillingComponentSupplier mManualFillingComponentSupplier;
     private @NonNull DataSharingTabManager mDataSharingTabManager;
     private @NonNull DataSharingTabSwitcherDelegate mDataSharingTabSwitcherDelegate;
-    private final OneshotSupplierImpl<TabGroupModelFilter> mOneshotFilterSupplier =
-            new OneshotSupplierImpl<>();
 
     // Activity tab observer that updates the current tab used by various UI components.
     private class RootUiTabObserver extends ActivityTabTabObserver {
@@ -419,7 +415,6 @@
                         mShareDelegateSupplier,
                         mWindowAndroid,
                         mActivity.getResources(),
-                        mOneshotFilterSupplier,
                         mTabGroupUiActionHandlerSupplier);
 
         initAppHeaderCoordinator(savedInstanceState);
@@ -622,14 +617,6 @@
         super.onFinishNativeInitialization();
         assert mLayoutManager != null;
 
-        new OneShotCallback<>(
-                mTabModelSelectorSupplier,
-                (selector) ->
-                        mOneshotFilterSupplier.set(
-                                (TabGroupModelFilter)
-                                        selector.getTabModelFilterProvider()
-                                                .getTabModelFilter(/* isIncognito= */ false)));
-
         mHistoryNavigationCoordinator =
                 HistoryNavigationCoordinator.create(
                         mWindowAndroid,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 1e4e8fe..b89c50d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -154,6 +154,7 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinatorFactory;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuDelegate;
+import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuObserver;
 import org.chromium.chrome.browser.ui.desktop_windowing.DesktopWindowStateProvider;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
@@ -2116,14 +2117,22 @@
         return mReadAloudControllerSupplier;
     }
 
+    /** Returns the {@link AppMenuHandler}. */
+    public @Nullable AppMenuHandler getAppMenuHandler() {
+        if (mAppMenuCoordinator != null) {
+            return mAppMenuCoordinator.getAppMenuHandler();
+        }
+        return null;
+    }
+
     /**
      * Saves relevant information that will be used to restore the UI state after the activity is
      * recreated. This is expected to be invoked in {@code Activity#onSaveInstanceState(Bundle)}.
      *
      * @param outState The {@link Bundle} that is used to save state information.
      * @param isRecreatingForTabletModeChange Whether the activity is recreated due to a fold
-     *         configuration change. {@code true} if the fold configuration changed, {@code false}
-     *         otherwise.
+     *     configuration change. {@code true} if the fold configuration changed, {@code false}
+     *     otherwise.
      */
     public void onSaveInstanceState(Bundle outState, boolean isRecreatingForTabletModeChange) {
         assert mTabModelSelectorSupplier.hasValue();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptRenderTest.java
index 82eeb7c..0b4b317 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptRenderTest.java
@@ -27,10 +27,8 @@
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.SyncServiceFactory;
@@ -50,7 +48,6 @@
  */
 @DoNotBatch(reason = "The tests can't be batched because they run for different set-ups.")
 @RunWith(ParameterizedRunner.class)
-@EnableFeatures({ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT})
 public class SaveUpdateAddressProfilePromptRenderTest extends BlankUiTestActivityTestCase {
     private static final long NATIVE_SAVE_UPDATE_ADDRESS_PROFILE_PROMPT_CONTROLLER = 100L;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragmentTest.java
index 387cf4d5..a0ebfed 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragmentTest.java
@@ -31,7 +31,6 @@
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.base.test.util.RequiresRestart;
 import org.chromium.chrome.browser.autofill.AutofillTestHelper;
-import org.chromium.chrome.browser.autofill.AutofillUiUtils.CardIconSize;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils.CardIconSpecs;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -41,6 +40,7 @@
 import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.R;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.payments.AccountType;
 import org.chromium.components.autofill.payments.BankAccount;
 import org.chromium.components.autofill.payments.PaymentInstrument;
@@ -106,8 +106,7 @@
                                     PIX_BANK_ACCOUNT_DISPLAY_ICON_URL,
                                     PIX_BANK_ACCOUNT_DISPLAY_ICON_BITMAP,
                                     CardIconSpecs.create(
-                                            ContextUtils.getApplicationContext(),
-                                            CardIconSize.LARGE));
+                                            ContextUtils.getApplicationContext(), ImageSize.LARGE));
                     // Set the Pix pref to true.
                     getPrefService().setBoolean(Pref.FACILITATED_PAYMENTS_PIX, true);
                 });
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 914ce87..aabf9d2 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
@@ -77,6 +77,7 @@
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteMatchBuilder;
 import org.chromium.components.omnibox.AutocompleteResult;
+import org.chromium.components.omnibox.OmniboxFeatures;
 import org.chromium.components.omnibox.OmniboxSuggestionType;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.content_public.common.ContentUrlConstants;
@@ -225,7 +226,28 @@
 
     @Test
     @SmallTest
-    public void testOmniboxSuggestionContainerAppears() throws Exception {
+    public void testOmniboxSuggestionContainerAppears_defaultRetainOmniboxOnFocus()
+            throws Exception {
+        testOmniboxSuggestionContainerAppears();
+    }
+
+    @Test
+    @SmallTest
+    public void testOmniboxSuggestionContainerAppears_shouldNotRetainOmniboxOnFocus()
+            throws Exception {
+        OmniboxFeatures.setShouldRetainOmniboxOnFocusForTesting(Boolean.FALSE);
+        testOmniboxSuggestionContainerAppears();
+    }
+
+    @Test
+    @SmallTest
+    public void testOmniboxSuggestionContainerAppears_shouldRetainOmniboxOnFocus()
+            throws Exception {
+        OmniboxFeatures.setShouldRetainOmniboxOnFocusForTesting(Boolean.TRUE);
+        testOmniboxSuggestionContainerAppears();
+    }
+
+    private void testOmniboxSuggestionContainerAppears() throws Exception {
         startSearchActivity();
 
         // Wait for the Activity to fully load.
@@ -233,14 +255,40 @@
         mTestDelegate.showSearchEngineDialogIfNeededCallback.waitForCallback(0);
         mTestDelegate.onFinishDeferredInitializationCallback.waitForCallback(0);
 
-        // Type in anything.  It should force the suggestions to appear.
+        // Focus empty omnibox.  It should force the suggestions to appear.
         mOmnibox.requestFocus();
-        verify(mAutocompleteController, times(1))
+        verify(mAutocompleteController)
                 .startZeroSuggest(
                         eq(""),
                         any(/* DSE URL*/ ),
                         eq(PageClassification.ANDROID_SEARCH_WIDGET_VALUE),
-                        eq(""));
+                        eq(""),
+                        /* isOnFocusContext= */ eq(false));
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () ->
+                        mOnSuggestionsReceivedListener.onSuggestionsReceived(
+                                buildDummyAutocompleteResult(), true));
+        mOmnibox.checkSuggestionsShown();
+
+        // Type in anything.
+        mOmnibox.typeText("text", /* commit= */ false);
+        mOmnibox.checkText(Matchers.equalTo("text"), null);
+
+        // Clear omnibox focus. This should always clear uncommitted text and hide suggestions.
+        mOmnibox.sendKey(KeyEvent.KEYCODE_ESCAPE);
+        mOmnibox.checkText(Matchers.isEmptyString(), null);
+        mOmnibox.checkSuggestionsShown(false);
+
+        // Refocusing omnibox should once again force the suggestions to appear.
+        mOmnibox.requestFocus();
+        verify(mAutocompleteController, times(2))
+                .startZeroSuggest(
+                        eq(""),
+                        any(/* DSE URL*/ ),
+                        eq(PageClassification.ANDROID_SEARCH_WIDGET_VALUE),
+                        eq(""),
+                        /* isOnFocusContext= */ eq(false));
 
         ThreadUtils.runOnUiThreadBlocking(
                 () ->
@@ -467,7 +515,8 @@
                         eq(""),
                         any(/* DSE URL */ ),
                         eq(PageClassification.ANDROID_SEARCH_WIDGET_VALUE),
-                        any());
+                        any(),
+                        /* isOnFocusContext= */ eq(false));
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
index 88b3a38..dfbec6a2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
@@ -6,18 +6,28 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.content.Intent;
 import android.graphics.Color;
 
+import androidx.fragment.app.Fragment;
 import androidx.test.filters.SmallTest;
+import androidx.test.runner.lifecycle.Stage;
 
+import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.ApplicationTestUtils;
+import org.chromium.base.test.util.Criteria;
+import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.about_settings.AboutChromeSettings;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.ui.test.util.DeviceRestriction;
 
@@ -46,4 +56,39 @@
                 Color.BLACK,
                 mSettingsActivityTestRule.getActivity().getWindow().getStatusBarColor());
     }
+
+    @Test
+    @SmallTest
+    @EnableFeatures({ChromeFeatureList.SETTINGS_SINGLE_ACTIVITY})
+    public void testStandaloneFragments() {
+        // Start the main settings, which is an embeddable fragment.
+        SettingsActivity activity = mSettingsActivityTestRule.startSettingsActivity();
+
+        // Open an embeddable fragment. This does NOT start a new activity.
+        final Intent intent1 =
+                SettingsIntentUtil.createIntent(
+                        activity, AboutChromeSettings.class.getName(), null);
+        activity.startActivity(intent1);
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    Criteria.checkThat(
+                            activity.getMainFragment(),
+                            Matchers.instanceOf(AboutChromeSettings.class));
+                });
+
+        // Open a standalone fragment. This will create a new activity.
+        final Intent intent2 =
+                SettingsIntentUtil.createIntent(activity, TestFragment.class.getName(), null);
+        ApplicationTestUtils.waitForActivityWithClass(
+                SettingsActivity.class, Stage.CREATED, () -> activity.startActivity(intent2));
+
+        // Open an embeddable fragment. This starts a new activity as the last fragment is
+        // standalone.
+        final Intent intent3 =
+                SettingsIntentUtil.createIntent(activity, MainSettings.class.getName(), null);
+        ApplicationTestUtils.waitForActivityWithClass(
+                SettingsActivity.class, Stage.CREATED, () -> activity.startActivity(intent3));
+    }
+
+    public static class TestFragment extends Fragment {}
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherTest.java
index 21ae92a..985b547 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherTest.java
@@ -20,9 +20,9 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features;
-import org.chromium.chrome.browser.autofill.AutofillUiUtils.CardIconSize;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils.CardIconSpecs;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.image_fetcher.test.TestImageFetcher;
 import org.chromium.url.GURL;
 
@@ -54,9 +54,9 @@
         GURL validUrl1 = new GURL("https://www.google.com/valid-image-url-1");
         GURL validUrl2 = new GURL("https://www.google.com/valid-image-url-2");
         CardIconSpecs cardIconSpecsSmall =
-                CardIconSpecs.create(ContextUtils.getApplicationContext(), CardIconSize.SMALL);
+                CardIconSpecs.create(ContextUtils.getApplicationContext(), ImageSize.SMALL);
         CardIconSpecs cardIconSpecsLarge =
-                CardIconSpecs.create(ContextUtils.getApplicationContext(), CardIconSize.LARGE);
+                CardIconSpecs.create(ContextUtils.getApplicationContext(), ImageSize.LARGE);
         GURL cachedValidUrlSmall1 =
                 AutofillUiUtils.getCreditCardIconUrlWithParams(
                         validUrl1, cardIconSpecsSmall.getWidth(), cardIconSpecsSmall.getHeight());
@@ -76,7 +76,8 @@
                 AutofillUiUtils.resizeAndAddRoundedCornersAndGreyBorder(
                         TEST_CARD_ART_IMAGE, cardIconSpecsLarge, true);
 
-        mImageFetcher.prefetchImages(new GURL[] {validUrl1, validUrl2});
+        mImageFetcher.prefetchImages(
+                new GURL[] {validUrl1, validUrl2}, new int[] {ImageSize.SMALL, ImageSize.LARGE});
         Map<String, Bitmap> cachedImages = mImageFetcher.getCachedImagesForTesting();
 
         // Each card art image is cached at 2 resolutions: 32x20 for the Keyboard Accessory, and
@@ -94,7 +95,7 @@
         mImageFetcher = new AutofillImageFetcher(new TestImageFetcher(null));
         GURL validUrl = new GURL("https://www.google.com/valid-image-url");
 
-        mImageFetcher.prefetchImages(new GURL[] {validUrl});
+        mImageFetcher.prefetchImages(new GURL[] {validUrl}, new int[] {ImageSize.SMALL});
 
         assertTrue(mImageFetcher.getCachedImagesForTesting().isEmpty());
     }
@@ -105,7 +106,8 @@
         GURL invalidUrl = new GURL("invalid-image-url");
         GURL emptyUrl = new GURL("");
 
-        mImageFetcher.prefetchImages(new GURL[] {invalidUrl, emptyUrl});
+        mImageFetcher.prefetchImages(
+                new GURL[] {invalidUrl, emptyUrl}, new int[] {ImageSize.SMALL});
 
         assertTrue(mImageFetcher.getCachedImagesForTesting().isEmpty());
     }
@@ -115,7 +117,7 @@
     public void testGetImagesIfAvailable() {
         GURL cardArtUrl = new GURL("https://www.google.com/card-art-url");
         CardIconSpecs cardIconSpecs =
-                CardIconSpecs.create(ContextUtils.getApplicationContext(), CardIconSize.LARGE);
+                CardIconSpecs.create(ContextUtils.getApplicationContext(), ImageSize.LARGE);
         Bitmap treatedImage =
                 AutofillUiUtils.resizeAndAddRoundedCornersAndGreyBorder(
                         TEST_CARD_ART_IMAGE, cardIconSpecs, true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java
index ca865ba..d9d4c1de 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java
@@ -25,6 +25,7 @@
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils.ErrorType;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.payments.LegalMessageLine;
 import org.chromium.url.GURL;
 
@@ -315,7 +316,7 @@
         Bitmap testImage = Bitmap.createBitmap(400, 300, Bitmap.Config.ARGB_8888);
         AutofillUiUtils.CardIconSpecs testSpecs =
                 AutofillUiUtils.CardIconSpecs.create(
-                        ContextUtils.getApplicationContext(), AutofillUiUtils.CardIconSize.LARGE);
+                        ContextUtils.getApplicationContext(), ImageSize.LARGE);
 
         Bitmap resizedTestImage =
                 AutofillUiUtils.resizeAndAddRoundedCornersAndGreyBorder(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
index 937eabbf..06c339d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
@@ -33,11 +33,8 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.SyncServiceFactory;
@@ -53,7 +50,6 @@
 /** Unit tests for {@link SaveUpdateAddressProfilePrompt}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@EnableFeatures({ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT})
 public class SaveUpdateAddressProfilePromptTest {
     private static final long NATIVE_SAVE_UPDATE_ADDRESS_PROFILE_PROMPT_CONTROLLER = 100L;
     private static final boolean NO_MIGRATION = false;
@@ -257,51 +253,6 @@
 
     @Test
     @SmallTest
-    @DisableFeatures(ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT)
-    public void setupAddressNickname_FeatureDisabled() {
-        createAndShowPrompt(false);
-
-        View dialog = mPrompt.getDialogViewForTesting();
-        assertEquals(dialog.findViewById(R.id.nickname_input_layout).getVisibility(), View.GONE);
-    }
-
-    @Test
-    @SmallTest
-    public void setupAddressNickname_FeatureEnabled() {
-        createAndShowPrompt(false);
-
-        View dialog = mPrompt.getDialogViewForTesting();
-        TextView nicknameInput = dialog.findViewById(R.id.nickname_input);
-
-        assertEquals(nicknameInput.getVisibility(), View.VISIBLE);
-        assertEquals(nicknameInput.getHint(), "Add a label");
-
-        nicknameInput.requestFocus();
-        assertEquals(nicknameInput.getHint(), "Label");
-
-        nicknameInput.setText("Text");
-        nicknameInput.clearFocus();
-        assertEquals(nicknameInput.getHint(), "Label");
-
-        nicknameInput.requestFocus();
-        assertEquals(nicknameInput.getHint(), "Label");
-
-        nicknameInput.setText("");
-        nicknameInput.clearFocus();
-        assertEquals(nicknameInput.getHint(), "Add a label");
-    }
-
-    @Test
-    @SmallTest
-    public void setupAddressNickname_NoNicknamesDuringUpdate() {
-        createAndShowPrompt(true);
-
-        View dialog = mPrompt.getDialogViewForTesting();
-        assertNull(dialog.findViewById(R.id.nickname_input_layout));
-    }
-
-    @Test
-    @SmallTest
     public void clickEditButton() {
         createAndShowPrompt(true);
         View dialog = mPrompt.getDialogViewForTesting();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
index af1d4ad..dd3cd67 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
@@ -117,6 +117,9 @@
     @Test
     @Feature("Tab Strip Group Context Menu")
     public void testListMenuItems() {
+        // Build custom view first to setup menu view.
+        mTabGroupContextMenuCoordinator.buildCustomView(mMenuView, /* isIncognito= */ false);
+
         ModelList modelList = new ModelList();
         mTabGroupContextMenuCoordinator.buildMenuActionItems(
                 modelList,
@@ -137,6 +140,9 @@
     @Test
     @Feature("Tab Strip Group Context Menu")
     public void testListMenuItems_Incognito() {
+        // Build custom view first to setup menu view.
+        mTabGroupContextMenuCoordinator.buildCustomView(mMenuView, /* isIncognito= */ false);
+
         ModelList modelList = new ModelList();
         mTabGroupContextMenuCoordinator.buildMenuActionItems(
                 modelList,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorderTest.java
index e910c55..d796dc5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorderTest.java
@@ -6,12 +6,18 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.text.TextUtils;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -20,7 +26,10 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.HistogramWatcher;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
+import org.chromium.chrome.browser.customtabs.CustomTabsOpenTimeRecorder.CloseCause;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 
@@ -40,9 +49,13 @@
 
     private CustomTabsOpenTimeRecorder mRecorder;
 
+    @Rule public JniMocker jniMocker = new JniMocker();
+    @Mock private CustomTabsOpenTimeRecorder.Natives mNativeMock;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        jniMocker.mock(CustomTabsOpenTimeRecorderJni.TEST_HOOKS, mNativeMock);
         ContextUtils.initApplicationContextForTests(mAppContext);
     }
 
@@ -81,4 +94,19 @@
                 CustomTabsOpenTimeRecorder.PACKAGE_NAME_EMPTY_1P,
                 mRecorder.getPackageName(true));
     }
+
+    @Test
+    public void testAuthTabClose() {
+        when(mIntent.isAuthTab()).thenReturn(true);
+        when(mIsCctFinishing.getAsBoolean()).thenReturn(true);
+        createRecorder();
+        HistogramWatcher histogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "CustomTabs.CloseCause", CloseCause.AUTH_TAB);
+        mRecorder.onStartWithNative();
+        mRecorder.onStopWithNative();
+        histogram.assertExpected();
+        verify(mNativeMock)
+                .recordCustomTabSession(anyLong(), anyString(), anyLong(), eq(false), anyBoolean());
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/settings/SettingsActivityUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/settings/SettingsActivityUnitTest.java
index 12a57a9..0d0c6b6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/settings/SettingsActivityUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/settings/SettingsActivityUnitTest.java
@@ -120,15 +120,12 @@
         mActivityScenario.moveToState(State.RESUMED);
 
         // Simulate opening a new fragment.
-        mActivityScenario.onActivity(
-                (activity) -> {
-                    Bundle args = new Bundle();
-                    args.putString(TestSettingsFragment.EXTRA_TITLE, "new title");
-                    Intent intent =
-                            SettingsIntentUtil.createIntent(
-                                    activity, TestSettingsFragment.class.getName(), args);
-                    activity.onNewIntent(intent);
-                });
+        Bundle args = new Bundle();
+        args.putString(TestSettingsFragment.EXTRA_TITLE, "new title");
+        Intent intent =
+                SettingsIntentUtil.createIntent(
+                        mSettingsActivity, TestSettingsFragment.class.getName(), args);
+        mSettingsActivity.onNewIntent(intent);
 
         // Wait for the UI update.
         ShadowLooper.runUiThreadTasks();
@@ -137,6 +134,29 @@
     }
 
     @Test
+    @EnableFeatures({ChromeFeatureList.SETTINGS_SINGLE_ACTIVITY})
+    public void testIntentFlags() {
+        launchSettingsActivity(TestSettingsFragment.class.getName());
+        mActivityScenario.moveToState(State.RESUMED);
+
+        Intent embeddableFragmentIntent =
+                SettingsIntentUtil.createIntent(
+                        mSettingsActivity, TestSettingsFragment.class.getName(), null);
+        assertEquals(
+                "Incorrect intent flags for embeddable fragments",
+                Intent.FLAG_ACTIVITY_SINGLE_TOP,
+                embeddableFragmentIntent.getFlags());
+
+        Intent standaloneFragmentIntent =
+                SettingsIntentUtil.createIntent(
+                        mSettingsActivity, TestStandaloneFragment.class.getName(), null);
+        assertEquals(
+                "Incorrect intent flags for standalone fragments",
+                0,
+                standaloneFragmentIntent.getFlags());
+    }
+
+    @Test
     public void testBackPress() throws TimeoutException {
         launchSettingsActivity(TestSettingsFragment.class.getName());
         assertTrue(
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index eeb10cfa..5f9098ca 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-130.0.6710.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-130.0.6712.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index d6571936..5feb456a 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1069,8 +1069,35 @@
         </message>
       </if>
 
+      <!-- Dice Web Signin Interception Bubble-->
+      <if expr="not use_titlecase">
+        <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT"
+          desc="Text asking the user to sign into Chromium while the Chromium Signin Bubble intercept is shown.">
+          Sign in to Chromium?
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_TITLE" desc="Title of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
+          Make Chromium your own
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT"
+          desc="Label of the Decline button of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
+          Use Chromium without an account
+        </message>
+      </if>
+      <if expr="use_titlecase">
+        <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT"
+          desc="Text asking the user to sign into Chromium while the Chromium Signin Bubble intercept is shown.">
+          Sign In to Chromium?
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_TITLE" desc="Title of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
+          Make Chromium Your Own
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT"
+          desc="Label of the Decline button of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
+          Use Chromium Without an Account
+        </message>
+      </if>
+
       <if expr="not chromeos_ash and not is_android and not chromeos_lacros">
-        <!-- Dice Web Signin Interception Bubble-->
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE" desc="Title of the web signin interception bubble">
           Continue in a new Chromium profile?
         </message>
@@ -1125,33 +1152,6 @@
         <message name="IDS_CLOSE_WARNING_FOR_CLEAR_COOKIES_ON_EXIT_TEXT" desc="Body of a notice displayed when closing the last Chrome window, to inform the user about a change in the way the 'Clear cookie on exit' setting works.">
           You’ll be signed out of most sites when you close all Chromium windows, except your Google Account if you’re signed in to Chromium. To let sites remember you, <ph name="SETTINGS_LINK">$1<ex>go to settings</ex></ph>.
         </message>
-
-        <if expr="not use_titlecase">
-          <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT"
-            desc="Text asking the user to sign into Chromium while the Chromium Signin Bubble intercept is shown.">
-            Sign in to Chromium?
-          </message>
-          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_TITLE" desc="Title of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
-            Make Chromium your own
-          </message>
-          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT"
-            desc="Label of the Decline button of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
-            Use Chromium without an account
-          </message>
-        </if>
-        <if expr="use_titlecase">
-          <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT"
-            desc="Text asking the user to sign into Chromium while the Chromium Signin Bubble intercept is shown.">
-            Sign In to Chromium?
-          </message>
-          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_TITLE" desc="Title of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
-            Make Chromium Your Own
-          </message>
-          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT"
-            desc="Label of the Decline button of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
-            Use Chromium Without an Account
-          </message>
-        </if>
         <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_ACCESSIBILITY_LABEL"
           desc="Accessibility label (read by the accessibility tools) of the avatar button when the Chromium Signin bubble is shown. The text implies that the expanded bubble underneath the button is closable by clicking on the button. The actual text is 'Sign in to Chromium?' with a bubble shown under it.">
           Click to close the Chromium sign-in dialog
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 0313791..3037186 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9721,6 +9721,17 @@
         Search tabs
       </message>
 
+      <!-- Strings for tab declutter -->
+      <message name="IDS_TOOLTIP_TAB_DECLUTTER" desc="The tooltip for the Tab Declutter button.">
+        Close unused tabs?
+      </message>
+      <message name="IDS_TAB_DECLUTTER" desc="The text label of the Tab Declutter button.">
+        Close unused tabs?
+      </message>
+      <message name="IDS_ACCNAME_TAB_DECLUTTER" desc="The accessible name for the Tab Declutter button." is_accessibility_with_no_ui="true">
+        Close unused tabs
+      </message>
+
       <!-- Strings for tab organization -->
       <message name="IDS_TOOLTIP_TAB_ORGANIZE" desc="The tooltip for the Tab Organization button.">
         Organize tabs?
@@ -11571,6 +11582,9 @@
       <message name="IDS_DECLUTTER_TITLE" desc="The header text for the declutter UI">
         Close unused tabs
       </message>
+      <message name="IDS_DECLUTTER_BODY" desc="The body text for the declutter UI">
+        These tabs have been unused for <ph name="COUNT">$1<ex>7</ex></ph> or more days.
+      </message>
 
       <!-- Strings for Window Titles in Menus -->
       <if expr="not use_titlecase">
@@ -17387,6 +17401,9 @@
     <message name="IDS_LENS_OVERLAY_COPY_TOAST_MESSAGE" desc="Notification shown in the Lens Overlay when the user has copied selected text to the clipboard.">
       Text copied
     </message>
+    <message name="IDS_LENS_OVERLAY_COPY_AS_IMAGE_TOAST_MESSAGE" desc="Notification shown in the Lens Overlay when the user has copied the selected image to the clipboard.">
+      Image copied
+    </message>
     <message name="IDS_LENS_OVERLAY_MORE_OPTIONS_BUTTON_LABEL" desc="Text that is shown in the tooltip of the more options button in the Lens Overlay.">
       More options
     </message>
@@ -17402,6 +17419,12 @@
     <message name="IDS_LENS_OVERLAY_SELECT_TEXT" desc="Command in the Lens Overlay context menu to select text.">
       Select text
     </message>
+    <message name="IDS_LENS_OVERLAY_COPY_AS_IMAGE" desc="Command in the Lens Overlay context menu to copy the selected screen region to the clipboard as an image.">
+      Copy as image
+    </message>
+    <message name="IDS_LENS_OVERLAY_SAVE_AS_IMAGE" desc="Command in the Lens Overlay context menu to save the selected screen region to disk as an image file.">
+      Save as image
+    </message>
     <message name="IDS_LENS_OVERLAY_LEARN_MORE" desc="Text that is shown in the more options menu to open the help center article.">
       Learn more
     </message>
diff --git a/chrome/app/generated_resources_grd/IDS_DECLUTTER_BODY.png.sha1 b/chrome/app/generated_resources_grd/IDS_DECLUTTER_BODY.png.sha1
new file mode 100644
index 0000000..0fe75433
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DECLUTTER_BODY.png.sha1
@@ -0,0 +1 @@
+dd58cb88340a2abb0335b359e1f5013a068ffa63
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_COPY_AS_IMAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_COPY_AS_IMAGE.png.sha1
new file mode 100644
index 0000000..e8ace66
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_COPY_AS_IMAGE.png.sha1
@@ -0,0 +1 @@
+6de8a4ef9f7b673bd6bd56a0c60a5f3baed44fed
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_COPY_AS_IMAGE_TOAST_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_COPY_AS_IMAGE_TOAST_MESSAGE.png.sha1
new file mode 100644
index 0000000..70a87a56
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_COPY_AS_IMAGE_TOAST_MESSAGE.png.sha1
@@ -0,0 +1 @@
+0408b03570ad6c9f15e5583de19c6c3647781907
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_SAVE_AS_IMAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_SAVE_AS_IMAGE.png.sha1
new file mode 100644
index 0000000..6c7ab6d
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_LENS_OVERLAY_SAVE_AS_IMAGE.png.sha1
@@ -0,0 +1 @@
+5cc10bfce478519d36e7237c245d41849595721d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TAB_DECLUTTER.png.sha1 b/chrome/app/generated_resources_grd/IDS_TAB_DECLUTTER.png.sha1
new file mode 100644
index 0000000..e005531
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_TAB_DECLUTTER.png.sha1
@@ -0,0 +1 @@
+ce537a9112b42f03e05ee02e7a1470de5d53a82d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_TOOLTIP_TAB_DECLUTTER.png.sha1 b/chrome/app/generated_resources_grd/IDS_TOOLTIP_TAB_DECLUTTER.png.sha1
new file mode 100644
index 0000000..976d6e8c
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_TOOLTIP_TAB_DECLUTTER.png.sha1
@@ -0,0 +1 @@
+32a9276253a4f36bd4dca3573b2709201b1691e8
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index c71da33..e7802f1a 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1028,8 +1028,30 @@
         </message>
       </if>
 
+      <!-- Dice Web Signin Interception Bubble-->
+      <if expr="not use_titlecase">
+        <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT" desc="Text asking the user to sign into Chrome while the Chrome Signin Bubble intercept is shown.">
+          Sign in to Chrome?
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_TITLE" desc="Title of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
+          Make Chrome your own
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT" desc="Label of the Decline button of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
+          Use Chrome without an account
+        </message>
+      </if>
+      <if expr="use_titlecase">
+        <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT" desc="Text asking the user to sign into Chrome while the Chrome Signin Bubble intercept is shown.">
+          Sign In to Chrome?
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_TITLE" desc="Title of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
+          Make Chrome Your Own
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT" desc="Label of the Decline button of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
+          Use Chrome Without an Account
+        </message>
+      </if>
       <if expr="not chromeos_ash and not is_android and not chromeos_lacros">
-        <!-- Dice Web Signin Interception Bubble-->
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE" desc="Title of the web signin interception bubble">
           Continue in a new Chrome profile?
         </message>
@@ -1084,29 +1106,6 @@
         <message name="IDS_CLOSE_WARNING_FOR_CLEAR_COOKIES_ON_EXIT_TEXT" desc="Body of a notice displayed when closing the last Chrome window, to inform the user about a change in the way the 'Clear cookie on exit' setting works.">
           You’ll be signed out of most sites when you close all Chrome windows, except your Google Account if you’re signed in to Chrome. To let sites remember you, <ph name="SETTINGS_LINK">$1<ex>go to settings</ex></ph>.
         </message>
-
-        <if expr="not use_titlecase">
-          <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT" desc="Text asking the user to sign into Chrome while the Chrome Signin Bubble intercept is shown.">
-            Sign in to Chrome?
-          </message>
-          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_TITLE" desc="Title of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
-            Make Chrome your own
-          </message>
-          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT" desc="Label of the Decline button of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
-            Use Chrome without an account
-          </message>
-        </if>
-        <if expr="use_titlecase">
-          <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT" desc="Text asking the user to sign into Chrome while the Chrome Signin Bubble intercept is shown.">
-            Sign In to Chrome?
-          </message>
-          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_TITLE" desc="Title of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
-            Make Chrome Your Own
-          </message>
-          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT" desc="Label of the Decline button of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
-            Use Chrome Without an Account
-          </message>
-        </if>
         <message name="IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_ACCESSIBILITY_LABEL"
           desc="Accessibility label (read by the accessibility tools) of the avatar button when the Chrome Signin bubble is shown. The text implies that the expanded bubble underneath the button is closable by clicking on the button. The actual text is 'Sign in to Chrome?' with a bubble shown under it.">
           Click to close the Chrome sign-in dialog
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 8b20319a..1fc17bd5 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -3513,9 +3513,6 @@
   <message name="IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_TITLE" desc="In Device Settings, the title of the style transfer toggle under the input section in audio settings subpage.">
     Studio-style mic
   </message>
-  <message name="IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_BADGE" desc="In Device Settings, the badge of the style transfer toggle under the input section in audio settings subpage.">
-    Experimental
-  </message>
   <message name="IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_DESCRIPTION" desc="In Device Settings, the description of the allowance of style transfer toggle under the input section in audio settings subpage.">
     Apply noise cancellation and audio improvements.
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_BADGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_BADGE.png.sha1
deleted file mode 100644
index f4f6c2b9..0000000
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_BADGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bd3d616404c098ffce62b77940703f18e0c79906
\ No newline at end of file
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp
index 4c253dd..1b2588f 100644
--- a/chrome/app/profiles_strings.grdp
+++ b/chrome/app/profiles_strings.grdp
@@ -658,6 +658,9 @@
     <message name="IDS_ENTERPRISE_OIDC_WELCOME_ERROR_SUBTITLE" desc="Subtitle of a section telling the user that there was an error while creating their work profile.">
       You can try again or contact your administrator for help
     </message>
+    <message name="IDS_ENTERPRISE_VALUE_PROPOSITION_SUBTITLE" desc="Subtitle the value proposition dialog for creating an enterprise profile.">
+      Sign in to securely separate your work and personal browsing. Increase your productivity while protecting sensitive company data.
+    </message>
 
     <!-- Supervised user profile signin IPH -->
     <!-- TODO(b/351333491): Mark them translateable once the strings are approved. -->
diff --git a/chrome/app/profiles_strings_grdp/IDS_ENTERPRISE_VALUE_PROPOSITION_SUBTITLE.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_ENTERPRISE_VALUE_PROPOSITION_SUBTITLE.png.sha1
new file mode 100644
index 0000000..1f4877e8
--- /dev/null
+++ b/chrome/app/profiles_strings_grdp/IDS_ENTERPRISE_VALUE_PROPOSITION_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+32fcfeb8567c08d353c07599fe97741b29612149
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c1e1a057..de16397 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -416,6 +416,8 @@
     "dips/dips_utils.h",
     "dips/persistent_repeating_timer.cc",
     "dips/persistent_repeating_timer.h",
+    "dips/stateful_bounce_counter.cc",
+    "dips/stateful_bounce_counter.h",
     "display_capture/captured_surface_control_permission_context.cc",
     "display_capture/captured_surface_control_permission_context.h",
     "display_capture/display_capture_permission_context.cc",
@@ -5015,7 +5017,6 @@
       "//chrome/browser/ash/login/session",
       "//chrome/browser/ash/login/signin",
       "//chrome/browser/ash/login/smart_lock",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/login/users",
       "//chrome/browser/ash/login/users/avatar",
       "//chrome/browser/ash/magic_boost",
@@ -5099,6 +5100,7 @@
       "//chrome/browser/ash/system_web_apps/apps/personalization_app",
       "//chrome/browser/ash/system_web_apps/apps/recorder_app",
       "//chrome/browser/ash/system_web_apps/types",
+      "//chrome/browser/ash/tpm",
       "//chrome/browser/ash/trusted_vault",
       "//chrome/browser/ash/u2f",
       "//chrome/browser/ash/url_handler",
@@ -5471,7 +5473,6 @@
       "//chrome/browser/ash/login/session",
       "//chrome/browser/ash/login/signin",
       "//chrome/browser/ash/login/smart_lock",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/login/users",
       "//chrome/browser/ash/login/users/avatar",
       "//chrome/browser/ash/magic_boost",
@@ -5544,6 +5545,7 @@
       "//chrome/browser/ash/system_web_apps/apps/media_app",
       "//chrome/browser/ash/system_web_apps/apps/personalization_app",
       "//chrome/browser/ash/system_web_apps/apps/recorder_app",
+      "//chrome/browser/ash/tpm",
       "//chrome/browser/ash/trusted_vault",
       "//chrome/browser/ash/u2f",
       "//chrome/browser/ash/url_handler",
@@ -6918,10 +6920,7 @@
       "first_run/upgrade_util_linux.cc",
       "first_run/upgrade_util_linux.h",
     ]
-    deps += [
-      "//chrome/app/theme:chrome_unscaled_resources_grit",
-      "//components/dbus/thread_linux",
-    ]
+    deps += [ "//chrome/app/theme:chrome_unscaled_resources_grit" ]
 
     if (is_chromeos_lacros) {
       sources += [
@@ -6937,9 +6936,6 @@
       sources += [
         "download/download_status_updater_linux.cc",
         "icon_loader_auralinux.cc",
-        "notifications/notification_platform_bridge_linux.cc",
-        "notifications/notification_platform_bridge_linux.h",
-        "platform_util_linux.cc",
         "shell_integration_linux.cc",
         "shell_integration_linux.h",
         "upgrade_detector/directory_monitor.cc",
@@ -6958,6 +6954,15 @@
       configs +=
           [ "//third_party/webrtc/modules/desktop_capture:pipewire_config" ]
     }
+
+    if (is_linux && use_dbus) {
+      sources += [
+        "notifications/notification_platform_bridge_linux.cc",
+        "notifications/notification_platform_bridge_linux.h",
+        "platform_util_linux.cc",
+      ]
+      deps += [ "//components/dbus/thread_linux" ]
+    }
   }
 
   if (is_posix) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index fcaf53a..992a07e5 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2240,12 +2240,16 @@
     {"force_tab_group", "true"}};
 const FeatureEntry::FeatureParam kEducationalTipModule_force_tab_group_sync[] =
     {{"force_tab_group_sync", "true"}};
+const FeatureEntry::FeatureParam kEducationalTipModule_force_quick_delete[] = {
+    {"force_quick_delete", "true"}};
 
 const FeatureEntry::FeatureVariation kEducationalTipModuleVariations[] = {
     {"Show tab group promo", kEducationalTipModule_force_tab_group,
      std::size(kEducationalTipModule_force_tab_group), nullptr},
     {"Show tab group sync promo", kEducationalTipModule_force_tab_group_sync,
      std::size(kEducationalTipModule_force_tab_group_sync), nullptr},
+    {"Show quick delete promo", kEducationalTipModule_force_quick_delete,
+     std::size(kEducationalTipModule_force_quick_delete), nullptr},
 };
 
 const FeatureEntry::FeatureParam
@@ -4059,6 +4063,24 @@
     {"with all task types", kDeferRendererTasksAfterInputAllTypesPolicyParam,
      std::size(kDeferRendererTasksAfterInputAllTypesPolicyParam), nullptr}};
 
+// LINT.IfChange(AutofillUploadCardRequestTimeouts)
+const FeatureEntry::FeatureParam
+    kAutofillUploadCardRequestTimeout_6Point5Seconds[] = {
+        {"autofill_upload_card_request_timeout_milliseconds", "6500"}};
+const FeatureEntry::FeatureParam kAutofillUploadCardRequestTimeout_7Seconds[] =
+    {{"autofill_upload_card_request_timeout_milliseconds", "7000"}};
+const FeatureEntry::FeatureParam kAutofillUploadCardRequestTimeout_9Seconds[] =
+    {{"autofill_upload_card_request_timeout_milliseconds", "9000"}};
+const FeatureEntry::FeatureVariation
+    kAutofillUploadCardRequestTimeoutOptions[] = {
+        {"6.5 seconds", kAutofillUploadCardRequestTimeout_6Point5Seconds,
+         std::size(kAutofillUploadCardRequestTimeout_6Point5Seconds), nullptr},
+        {"7 seconds", kAutofillUploadCardRequestTimeout_7Seconds,
+         std::size(kAutofillUploadCardRequestTimeout_7Seconds), nullptr},
+        {"9 seconds", kAutofillUploadCardRequestTimeout_9Seconds,
+         std::size(kAutofillUploadCardRequestTimeout_9Seconds), nullptr}};
+// LINT.ThenChange(//ios/chrome/browser/flags/about_flags.mm:AutofillUploadCardRequestTimeouts)
+
 // LINT.IfChange(AutofillVcnEnrollRequestTimeouts)
 const FeatureEntry::FeatureParam kAutofillVcnEnrollRequestTimeout_5Seconds[] = {
     {"autofill_vcn_enroll_request_timeout_milliseconds", "5000"}};
@@ -11692,6 +11714,14 @@
      PLATFORM_FEATURE_NAME_TYPE("CrOSLateBootAllowFpmcuBetaFirmware")},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+    {"autofill-upload-card-request-timeout",
+     flag_descriptions::kAutofillUploadCardRequestTimeoutName,
+     flag_descriptions::kAutofillUploadCardRequestTimeoutDescription, kOsAll,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(
+         autofill::features::kAutofillUploadCardRequestTimeout,
+         kAutofillUploadCardRequestTimeoutOptions,
+         "AutofillUploadCardRequestTimeout")},
+
     {"autofill-vcn-enroll-request-timeout",
      flag_descriptions::kAutofillVcnEnrollRequestTimeoutName,
      flag_descriptions::kAutofillVcnEnrollRequestTimeoutDescription, kOsAll,
diff --git a/chrome/browser/accessibility/accessibility_extension_api_ash.cc b/chrome/browser/accessibility/accessibility_extension_api_ash.cc
index cb0fbda7..ddcd7a91 100644
--- a/chrome/browser/accessibility/accessibility_extension_api_ash.cc
+++ b/chrome/browser/accessibility/accessibility_extension_api_ash.cc
@@ -602,10 +602,12 @@
   bool dictation_enabled = AccessibilityManager::Get()->IsDictationEnabled();
   bool from_accessibility_common =
       extension_id() == extension_misc::kAccessibilityCommonExtensionId;
-  if (dictation_enabled && from_accessibility_common &&
+  bool facegaze_enabled = AccessibilityManager::Get()->IsFaceGazeEnabled();
+  if ((dictation_enabled || facegaze_enabled) && from_accessibility_common &&
       params->use_rewriters.has_value() && params->use_rewriters.value()) {
-    // TODO(b/259397131): Remove the `useRewriters` property and remove this
-    // if statement.
+    // TODO(b/259397131): Remove the `useRewriters` property.
+    // Call SendEventToSink so that the event can be processed by event
+    // rewriters, like Game Controls.
     host->SendEventToSink(&synthetic_key_event);
   } else {
     host->DeliverEventToSink(&synthetic_key_event);
diff --git a/chrome/browser/accessibility/soda_installer_impl.cc b/chrome/browser/accessibility/soda_installer_impl.cc
index c139f4f0..81a82b4 100644
--- a/chrome/browser/accessibility/soda_installer_impl.cc
+++ b/chrome/browser/accessibility/soda_installer_impl.cc
@@ -41,7 +41,14 @@
 
 base::FilePath SodaInstallerImpl::GetLanguagePath(
     const std::string& language) const {
-  DLOG(FATAL) << "GetLanguagePath not supported on this platform";
+  std::optional<speech::SodaLanguagePackComponentConfig> config =
+      speech::GetLanguageComponentConfig(language);
+  if (config.has_value() &&
+      config.value().language_code != speech::LanguageCode::kNone) {
+    return g_browser_process->local_state()->GetFilePath(
+        config.value().config_path_pref);
+  }
+
   return base::FilePath();
 }
 
diff --git a/chrome/browser/accessibility/soda_installer_impl.h b/chrome/browser/accessibility/soda_installer_impl.h
index e1c0691..45938ba 100644
--- a/chrome/browser/accessibility/soda_installer_impl.h
+++ b/chrome/browser/accessibility/soda_installer_impl.h
@@ -37,7 +37,6 @@
   // Currently only implemented in the chromeos-specific subclass.
   base::FilePath GetSodaBinaryPath() const override;
 
-  // Currently only implemented in the chromeos-specific subclass.
   base::FilePath GetLanguagePath(const std::string& language) const override;
 
   // SodaInstaller:
diff --git a/chrome/browser/after_startup_task_utils.cc b/chrome/browser/after_startup_task_utils.cc
index bdc3c9f..d2cc5299 100644
--- a/chrome/browser/after_startup_task_utils.cc
+++ b/chrome/browser/after_startup_task_utils.cc
@@ -22,7 +22,7 @@
 #include "content/public/browser/browser_thread.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
index 0b34af9..8d20546 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.cc
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -238,7 +238,8 @@
     const JavaParamRef<jstring>& j_omnibox_text,
     const JavaParamRef<jstring>& j_current_url,
     jint j_page_classification,
-    const JavaParamRef<jstring>& j_current_title) {
+    const JavaParamRef<jstring>& j_current_title,
+    bool is_on_focus_context) {
   // Prevents double triggering of zero suggest when OnOmniboxFocused is issued
   // in quick succession (due to odd timing in the Android focus callbacks).
   if (!autocomplete_controller_->done()) {
@@ -260,9 +261,9 @@
   auto page_class =
       OmniboxEventProto::PageClassification(j_page_classification);
   const bool interaction_clobber_focus_type =
-      base::FeatureList::IsEnabled(
-          omnibox::kOmniboxOnClobberFocusTypeOnContent) &&
-      !omnibox::IsNTPPage(page_class);
+      !(omnibox::IsNTPPage(page_class) ||
+        (is_on_focus_context &&
+         base::FeatureList::IsEnabled(omnibox::kRetainOmniboxOnFocus)));
   if (interaction_clobber_focus_type) {
     omnibox_text.clear();
   }
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.h b/chrome/browser/android/omnibox/autocomplete_controller_android.h
index b4f95c5..4638b2518 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.h
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.h
@@ -60,7 +60,8 @@
       const base::android::JavaParamRef<jstring>& j_omnibox_text,
       const base::android::JavaParamRef<jstring>& j_current_url,
       jint j_page_classification,
-      const base::android::JavaParamRef<jstring>& j_current_title);
+      const base::android::JavaParamRef<jstring>& j_current_title,
+      bool is_on_focus_context);
   void Stop(JNIEnv* env, bool clear_result);
   void ResetSession(JNIEnv* env);
 
@@ -119,6 +120,14 @@
 
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject() const;
 
+  template <typename T>
+  T* SetAutocompleteControllerForTesting(
+      std::unique_ptr<T> autocomplete_controller) {
+    T* result = autocomplete_controller.get();
+    autocomplete_controller_ = std::move(autocomplete_controller);
+    return result;
+  }
+
   class Factory : public ProfileKeyedServiceFactory {
    public:
     static AutocompleteControllerAndroid* GetForProfile(Profile* profile);
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android_unittest.cc b/chrome/browser/android/omnibox/autocomplete_controller_android_unittest.cc
new file mode 100644
index 0000000..3223770b
--- /dev/null
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android_unittest.cc
@@ -0,0 +1,157 @@
+// 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/android/omnibox/autocomplete_controller_android.h"
+
+#include <memory>
+#include <optional>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/memory/raw_ptr.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/fake_autocomplete_provider_client.h"
+#include "components/omnibox/common/omnibox_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
+#include "third_party/metrics_proto/omnibox_focus_type.pb.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Aliases.
+using ::testing::AllOf;
+using ::testing::Bool;
+using ::testing::Combine;
+using ::testing::Conditional;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::NiceMock;
+using ::testing::Property;
+using ::testing::Values;
+using ::testing::WithParamInterface;
+
+// Mocks -----------------------------------------------------------------------
+
+class MockAutocompleteController : public AutocompleteController {
+ public:
+  MockAutocompleteController()
+      : AutocompleteController(
+            std::make_unique<FakeAutocompleteProviderClient>(),
+            /*provider_types=*/0) {}
+
+  // AutocompleteController:
+  MOCK_METHOD(void, Start, (const AutocompleteInput&), (override));
+};
+
+}  // namespace
+
+// AutocompleteControllerAndroidTest -------------------------------------------
+
+// Base class for tests of `AutocompleteControllerAndroid`.
+class AutocompleteControllerAndroidTest
+    : public ChromeRenderViewHostTestHarness {
+ public:
+  AutocompleteControllerAndroid* controller() { return controller_; }
+  NiceMock<MockAutocompleteController>* mock() { return mock_; }
+
+ private:
+  // ChromeRenderViewHostTestHarness:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    // Initialize autocomplete controller/mock.
+    controller_ =
+        AutocompleteControllerAndroid::Factory::GetForProfile(profile());
+    ASSERT_TRUE(controller_);
+    mock_ = controller_->SetAutocompleteControllerForTesting(
+        std::make_unique<NiceMock<MockAutocompleteController>>());
+  }
+
+  raw_ptr<AutocompleteControllerAndroid> controller_ = nullptr;
+  raw_ptr<NiceMock<MockAutocompleteController>> mock_ = nullptr;
+};
+
+// AutocompleteControllerAndroidOmniboxFocusTest -------------------------------
+
+// Base class for parameterized tests of `AutocompleteControllerAndroid` which
+// assert expectations regarding omnibox focus behavior.
+class AutocompleteControllerAndroidOmniboxFocusTest
+    : public AutocompleteControllerAndroidTest,
+      public WithParamInterface<std::tuple<
+          /*is_ntp_page=*/bool,
+          /*is_on_focus_context=*/bool,
+          /*is_retain_omnibox_on_focus_enabled=*/std::optional<bool>>> {
+ public:
+  AutocompleteControllerAndroidOmniboxFocusTest() {
+    if (const auto& is_retain_omnibox_on_focus_enabled =
+            IsRetainOmniboxOnFocusEnabled()) {
+      scoped_feature_list_.InitWithFeatureState(
+          omnibox::kRetainOmniboxOnFocus,
+          is_retain_omnibox_on_focus_enabled.value());
+    }
+  }
+
+  bool IsNtpPage() const { return std::get<0>(GetParam()); }
+
+  bool IsOnFocusContext() const { return std::get<1>(GetParam()); }
+
+  const std::optional<bool>& IsRetainOmniboxOnFocusEnabled() const {
+    return std::get<2>(GetParam());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         AutocompleteControllerAndroidOmniboxFocusTest,
+                         Combine(/*is_ntp_page=*/Bool(),
+                                 /*is_on_focus_context=*/Bool(),
+                                 /*is_retain_omnibox_on_focus_enabled=*/
+                                 Values(true, false, std::nullopt)));
+
+// Tests -----------------------------------------------------------------------
+
+TEST_P(AutocompleteControllerAndroidOmniboxFocusTest, OnOmniboxFocused) {
+  using OEP = metrics::OmniboxEventProto;
+  using OFT = metrics::OmniboxFocusType;
+
+  bool is_ntp_page = IsNtpPage();
+  bool is_on_focus_context = IsOnFocusContext();
+  bool is_retain_omnibox_on_focus_enabled =
+      IsRetainOmniboxOnFocusEnabled().value_or(false);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  auto j_omnibox_text = base::android::ConvertUTF16ToJavaString(env, u"text");
+  auto j_current_url = base::android::ConvertUTF16ToJavaString(env, u"url");
+  auto j_current_title = base::android::ConvertUTF16ToJavaString(env, u"title");
+  jint j_page_classification = IsNtpPage() ? OEP::NTP : OEP::OTHER;
+
+  bool expect_interaction_clobber_focus_type =
+      !(is_ntp_page ||
+        (is_on_focus_context && is_retain_omnibox_on_focus_enabled));
+
+  EXPECT_CALL(
+      *mock(),
+      Start(AllOf(Property(&AutocompleteInput::text,
+                           Conditional(expect_interaction_clobber_focus_type,
+                                       IsEmpty(), Eq(u"text"))),
+                  Property(&AutocompleteInput::current_url, Eq(GURL("url"))),
+                  Property(&AutocompleteInput::current_title, Eq(u"title")),
+                  Property(&AutocompleteInput::focus_type,
+                           Conditional(expect_interaction_clobber_focus_type,
+                                       Eq(OFT::INTERACTION_CLOBBER),
+                                       Eq(OFT::INTERACTION_FOCUS))))));
+
+  controller()->OnOmniboxFocused(
+      env, base::android::JavaParamRef<jstring>(env, j_omnibox_text.obj()),
+      base::android::JavaParamRef<jstring>(env, j_current_url.obj()),
+      j_page_classification,
+      base::android::JavaParamRef<jstring>(env, j_current_title.obj()),
+      is_on_focus_context);
+}
diff --git a/chrome/browser/apps/almanac_api_client/BUILD.gn b/chrome/browser/apps/almanac_api_client/BUILD.gn
index 0b18413..80889a5 100644
--- a/chrome/browser/apps/almanac_api_client/BUILD.gn
+++ b/chrome/browser/apps/almanac_api_client/BUILD.gn
@@ -76,6 +76,7 @@
     ":almanac_api_client",
     "//base",
     "//chrome/browser/apps/almanac_api_client/proto",
+    "//chrome/browser/apps/almanac_api_client/proto:test_proto",
     "//chrome/common:channel_info",
     "//chrome/test:test_support",
     "//chromeos/ash/components/system",
diff --git a/chrome/browser/apps/almanac_api_client/almanac_api_util.cc b/chrome/browser/apps/almanac_api_client/almanac_api_util.cc
index 756c989..6cccfbf8 100644
--- a/chrome/browser/apps/almanac_api_client/almanac_api_util.cc
+++ b/chrome/browser/apps/almanac_api_client/almanac_api_util.cc
@@ -115,12 +115,11 @@
 void QueryAlmanacApiRaw(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
-    const std::string& request_body,
     std::string_view endpoint_suffix,
     int max_response_size,
     std::optional<std::string> error_histogram_name,
-    base::OnceCallback<void(base::expected<std::string, QueryError>)>
-        callback) {
+    base::OnceCallback<void(base::expected<std::string, QueryError>)> callback,
+    const std::string& request_body) {
   std::unique_ptr<network::SimpleURLLoader> loader = apps::GetAlmanacUrlLoader(
       traffic_annotation, request_body, endpoint_suffix);
 
diff --git a/chrome/browser/apps/almanac_api_client/almanac_api_util.h b/chrome/browser/apps/almanac_api_client/almanac_api_util.h
index 3944c3f..c58c4c2 100644
--- a/chrome/browser/apps/almanac_api_client/almanac_api_util.h
+++ b/chrome/browser/apps/almanac_api_client/almanac_api_util.h
@@ -11,8 +11,12 @@
 #include <string_view>
 
 #include "base/functional/callback.h"
+#include "base/functional/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/types/expected.h"
+#include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
+#include "chrome/browser/apps/almanac_api_client/device_info_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/mojom/url_response_head.mojom-forward.h"
 #include "third_party/abseil-cpp/absl/status/status.h"
@@ -55,14 +59,87 @@
 void QueryAlmanacApiRaw(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
-    const std::string& request_body,
     std::string_view endpoint_suffix,
     int max_response_size,
     std::optional<std::string> error_histogram_name,
-    base::OnceCallback<void(base::expected<std::string, QueryError>)> callback);
+    base::OnceCallback<void(base::expected<std::string, QueryError>)> callback,
+    const std::string& request_body);
+
+template <typename T>
+base::expected<T, QueryError> ParseResponse(
+    base::expected<std::string, QueryError> string_response) {
+  if (!string_response.has_value()) {
+    return base::unexpected(std::move(string_response).error());
+  }
+
+  T result;
+  if (result.ParseFromString(*string_response)) {
+    return base::ok(result);
+  }
+
+  return base::unexpected(QueryError{
+      QueryError::kBadResponse,
+      "Parsing failed",
+  });
+}
 
 }  // namespace internal
 
+// Sends a network fetch to the `endpoint_suffix` of the Almanac server, with a
+// client context attached, and calls `callback` with the result.
+//
+// `partial_request` is a instance of a RequestProto message for an Almanac
+// endpoint which has all non-context fields filled. This method will attach a
+// device and user context, serialize the proto, and attach it to the network
+// request. RequestProto must have fields for the device_context and
+// user_context, e.g.:
+//
+//   optional ClientDeviceContext device_context = 1;
+//   optional ClientUserContext user_context = 2;
+//
+// Any response from the server will be parsed into a ResponseProto and
+// returned. If any HTTP or other failure occurs, a QueryError is returned
+// instead.
+//
+// Adds HTTP response codes to UMA if an `error_histogram_name` is specified.
+// The histogram must be defined in a histograms.xml file using enum
+// CombinedHttpResponseAndNetErrorCode.
+template <typename RequestProto, typename ResponseProto>
+void QueryAlmanacApiWithContext(
+    Profile* profile,
+    std::string_view endpoint_suffix,
+    RequestProto partial_request,
+    const net::NetworkTrafficAnnotationTag& traffic_annotation,
+    int max_response_size,
+    std::optional<std::string> error_histogram_name,
+    base::OnceCallback<void(base::expected<ResponseProto, QueryError>)>
+        callback) {
+  DeviceInfoManager* manager = DeviceInfoManagerFactory::GetForProfile(profile);
+  if (!manager) {
+    std::move(callback).Run(base::unexpected(
+        QueryError{QueryError::kBadRequest,
+                   "Unavailable for incognito and system profiles"}));
+  }
+
+  base::OnceCallback<void(const std::string&)> query_with_request_body =
+      base::BindOnce(&internal::QueryAlmanacApiRaw,
+                     profile->GetURLLoaderFactory(), traffic_annotation,
+                     endpoint_suffix, max_response_size, error_histogram_name,
+                     base::BindOnce(&internal::ParseResponse<ResponseProto>)
+                         .Then(std::move(callback)));
+
+  manager->GetDeviceInfo(base::BindOnce(
+                             [](RequestProto partial_request, DeviceInfo info) {
+                               *partial_request.mutable_device_context() =
+                                   info.ToDeviceContext();
+                               *partial_request.mutable_user_context() =
+                                   info.ToUserContext();
+                               return partial_request.SerializeAsString();
+                             },
+                             std::move(partial_request))
+                             .Then(std::move(query_with_request_body)));
+}
+
 // Sends a network fetch to the `endpoint_suffix` of the Almanac server with
 // `request_body` and returns the T proto response or QueryError if there was a
 // failure.
@@ -79,24 +156,10 @@
     std::optional<std::string> error_histogram_name,
     base::OnceCallback<void(base::expected<T, QueryError>)> callback) {
   internal::QueryAlmanacApiRaw(
-      url_loader_factory, traffic_annotation, request_body, endpoint_suffix,
+      url_loader_factory, traffic_annotation, endpoint_suffix,
       max_response_size, error_histogram_name,
-      base::BindOnce([](base::expected<std::string, QueryError> string_response)
-                         -> base::expected<T, QueryError> {
-        if (!string_response.has_value()) {
-          return base::unexpected(std::move(string_response).error());
-        }
-
-        T result;
-        if (result.ParseFromString(*string_response)) {
-          return base::ok(result);
-        }
-
-        return base::unexpected(QueryError{
-            QueryError::kBadResponse,
-            "Parsing failed",
-        });
-      }).Then(std::move(callback)));
+      base::BindOnce(&internal::ParseResponse<T>).Then(std::move(callback)),
+      request_body);
 }
 
 }  // namespace apps
diff --git a/chrome/browser/apps/almanac_api_client/almanac_api_util_unittest.cc b/chrome/browser/apps/almanac_api_client/almanac_api_util_unittest.cc
index ecc39e4..dc17b47 100644
--- a/chrome/browser/apps/almanac_api_client/almanac_api_util_unittest.cc
+++ b/chrome/browser/apps/almanac_api_client/almanac_api_util_unittest.cc
@@ -7,13 +7,19 @@
 #include <string>
 #include <string_view>
 
+#include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
+#include "chrome/browser/apps/almanac_api_client/proto/test_request.pb.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "content/public/test/browser_task_environment.h"
 #include "net/base/net_errors.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -31,6 +37,13 @@
   AlmanacApiUtilTest() = default;
   ~AlmanacApiUtilTest() override = default;
 
+  void SetUp() override {
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetSharedURLLoaderFactory(
+        test_url_loader_factory_.GetSafeWeakWrapper());
+    profile_ = profile_builder.Build();
+  }
+
   base::expected<TestProto, QueryError> QueryEndpoint() {
     base::test::TestFuture<base::expected<TestProto, QueryError>> future;
     QueryAlmanacApi<TestProto>(
@@ -41,9 +54,21 @@
     return future.Get();
   }
 
+  base::expected<TestProto, QueryError> QueryEndpointWithContext(
+      proto::TestRequest request) {
+    base::test::TestFuture<base::expected<TestProto, QueryError>> future;
+    QueryAlmanacApiWithContext<proto::TestRequest, TestProto>(
+        profile_.get(), "endpoint", request, TRAFFIC_ANNOTATION_FOR_TESTS,
+        /*max_response_size=*/1024 * 1024,
+        /*error_histogram_name=*/std::nullopt, future.GetCallback());
+    return future.Get();
+  }
+
  protected:
   network::TestURLLoaderFactory test_url_loader_factory_;
-  base::test::SingleThreadTaskEnvironment task_environment_;
+  content::BrowserTaskEnvironment task_environment_;
+  ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
+  std::unique_ptr<TestingProfile> profile_;
 };
 
 TEST_F(AlmanacApiUtilTest, GetEndpointUrl) {
@@ -55,6 +80,39 @@
             "https://chromeosalmanac-pa.googleapis.com/v1/app-preload");
 }
 
+TEST_F(AlmanacApiUtilTest, QueryAlmanacApi_RequestSettings) {
+  std::string method;
+  std::optional<std::string> method_override_header;
+  std::optional<std::string> content_type;
+  std::string body;
+  GURL url;
+
+  base::RunLoop run_loop;
+  test_url_loader_factory_.SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        url = request.url;
+        content_type =
+            request.headers.GetHeader(net::HttpRequestHeaders::kContentType);
+        method_override_header =
+            request.headers.GetHeader("X-HTTP-Method-Override");
+        method = request.method;
+        body = network::GetUploadData(request);
+        run_loop.Quit();
+      }));
+
+  QueryAlmanacApi<TestProto>(
+      test_url_loader_factory_.GetSafeWeakWrapper(),
+      TRAFFIC_ANNOTATION_FOR_TESTS, "serialized proto", "endpoint",
+      /*max_response_size=*/1024 * 1024,
+      /*error_histogram_name=*/std::nullopt, base::DoNothing());
+  run_loop.Run();
+  EXPECT_EQ(url, GetAlmanacEndpointUrl("endpoint"));
+  EXPECT_EQ(method, "POST");
+  EXPECT_EQ(method_override_header, "GET");
+  EXPECT_EQ(content_type, "application/x-protobuf");
+  EXPECT_EQ(body, "serialized proto");
+}
+
 TEST_F(AlmanacApiUtilTest, QueryAlmanacApi_ConnectionFailed) {
   test_url_loader_factory_.AddResponse(
       GetAlmanacEndpointUrl("endpoint"), network::mojom::URLResponseHead::New(),
@@ -95,5 +153,27 @@
   EXPECT_EQ(QueryEndpoint(), base::ok(TestProto()));
 }
 
+TEST_F(AlmanacApiUtilTest, QueryAlmanacApiWithContext_AddsContext) {
+  proto::TestRequest request;
+  request.set_package_id("web:foo");
+
+  proto::TestRequest sent_request;
+  base::RunLoop run_loop;
+
+  test_url_loader_factory_.SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        ASSERT_TRUE(
+            sent_request.ParseFromString(network::GetUploadData(request)));
+      }));
+
+  test_url_loader_factory_.AddResponse(GetAlmanacEndpointUrl("endpoint").spec(),
+                                       /*content=*/"valid", net::HTTP_OK);
+  EXPECT_EQ(QueryEndpointWithContext(request), base::ok(TestProto()));
+
+  EXPECT_EQ(sent_request.package_id(), "web:foo");
+  EXPECT_TRUE(sent_request.has_device_context());
+  EXPECT_TRUE(sent_request.has_user_context());
+}
+
 }  // namespace
 }  // namespace apps
diff --git a/chrome/browser/apps/almanac_api_client/proto/BUILD.gn b/chrome/browser/apps/almanac_api_client/proto/BUILD.gn
index da662b78..678af28a 100644
--- a/chrome/browser/apps/almanac_api_client/proto/BUILD.gn
+++ b/chrome/browser/apps/almanac_api_client/proto/BUILD.gn
@@ -9,3 +9,12 @@
 
   sources = [ "client_context.proto" ]
 }
+
+proto_library("test_proto") {
+  proto_in_dir = "//"
+  testonly = true
+
+  sources = [ "test_request.proto" ]
+
+  link_deps = [ ":proto" ]
+}
diff --git a/chrome/browser/apps/almanac_api_client/proto/test_request.proto b/chrome/browser/apps/almanac_api_client/proto/test_request.proto
new file mode 100644
index 0000000..be63ff3e
--- /dev/null
+++ b/chrome/browser/apps/almanac_api_client/proto/test_request.proto
@@ -0,0 +1,19 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package apps.proto;
+
+import "chrome/browser/apps/almanac_api_client/proto/client_context.proto";
+
+// This proto is used for unit tests and is not included in non-test builds.
+
+message TestRequest {
+  optional string package_id = 1;
+  optional ClientDeviceContext device_context = 2;
+  optional ClientUserContext user_context = 3;
+}
diff --git a/chrome/browser/apps/app_discovery_service/almanac_fetcher.cc b/chrome/browser/apps/app_discovery_service/almanac_fetcher.cc
index 549d87db..1146234 100644
--- a/chrome/browser/apps/app_discovery_service/almanac_fetcher.cc
+++ b/chrome/browser/apps/app_discovery_service/almanac_fetcher.cc
@@ -73,7 +73,6 @@
 AlmanacFetcher::AlmanacFetcher(Profile* profile,
                                std::unique_ptr<AlmanacIconCache> icon_cache)
     : profile_(profile),
-      device_info_manager_(std::make_unique<DeviceInfoManager>(profile)),
       icon_cache_(std::move(icon_cache)) {
   // The whole feature would not work unless the build includes the Google
   // Chrome API key or this is a test environment as the server call would fail.
@@ -140,21 +139,15 @@
 
 void AlmanacFetcher::DownloadApps() {
   if ((base::Time::Now() - GetLastAppsUpdateTime()).InHours() >= 24) {
-    device_info_manager_->GetDeviceInfo(base::BindOnce(
-        &AlmanacFetcher::OnGetDeviceInfo, weak_factory_.GetWeakPtr()));
+    launcher_app_almanac_endpoint::GetApps(
+        profile_, base::BindOnce(&AlmanacFetcher::OnServerResponse,
+                                 weak_factory_.GetWeakPtr()));
   } else {
     proto_file_manager_->ReadProtoFromFile(base::BindOnce(
         &AlmanacFetcher::OnAppsUpdate, weak_factory_.GetWeakPtr()));
   }
 }
 
-void AlmanacFetcher::OnGetDeviceInfo(DeviceInfo device_info) {
-  launcher_app_almanac_endpoint::GetApps(
-      device_info, profile_->GetURLLoaderFactory(),
-      base::BindOnce(&AlmanacFetcher::OnServerResponse,
-                     weak_factory_.GetWeakPtr()));
-}
-
 void AlmanacFetcher::OnServerResponse(
     std::optional<proto::LauncherAppResponse> response) {
   if (response.has_value()) {
diff --git a/chrome/browser/apps/app_discovery_service/almanac_fetcher.h b/chrome/browser/apps/app_discovery_service/almanac_fetcher.h
index fcb0016b..b8d51a60 100644
--- a/chrome/browser/apps/app_discovery_service/almanac_fetcher.h
+++ b/chrome/browser/apps/app_discovery_service/almanac_fetcher.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_APPS_APP_DISCOVERY_SERVICE_ALMANAC_FETCHER_H_
 #define CHROME_BROWSER_APPS_APP_DISCOVERY_SERVICE_ALMANAC_FETCHER_H_
 
-#include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
 #include "chrome/browser/apps/almanac_api_client/proto_file_manager.h"
 #include "chrome/browser/apps/app_discovery_service/almanac_api/launcher_app.pb.h"
 #include "chrome/browser/apps/app_discovery_service/app_discovery_util.h"
@@ -58,9 +57,6 @@
   // caches, and notifies the class subscribers.
   void DownloadApps();
 
-  // Calls the Almanac server with the device information provided.
-  void OnGetDeviceInfo(DeviceInfo device_info);
-
   // Writes the response to disk if the call to the server succeeded or reads
   // the cached data otherwise.
   void OnServerResponse(std::optional<proto::LauncherAppResponse> response);
@@ -78,7 +74,6 @@
 
   ResultCallbackList subscribers_;
 
-  std::unique_ptr<DeviceInfoManager> device_info_manager_;
   std::unique_ptr<ProtoFileManager<proto::LauncherAppResponse>>
       proto_file_manager_;
   std::unique_ptr<AlmanacIconCache> icon_cache_;
diff --git a/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint.cc b/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint.cc
index ec671e7..2cd7231 100644
--- a/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint.cc
+++ b/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint.cc
@@ -6,7 +6,6 @@
 
 #include "base/functional/callback.h"
 #include "chrome/browser/apps/almanac_api_client/almanac_api_util.h"
-#include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
 #include "chrome/browser/apps/app_discovery_service/almanac_api/launcher_app.pb.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -53,15 +52,6 @@
       }
     )");
 
-// Builds the Launcher App request from the given device info.
-std::string BuildRequestBody(const DeviceInfo& info) {
-  proto::LauncherAppRequest request_proto;
-  *request_proto.mutable_device_context() = info.ToDeviceContext();
-  *request_proto.mutable_user_context() = info.ToUserContext();
-
-  return request_proto.SerializeAsString();
-}
-
 std::optional<proto::LauncherAppResponse> MakeResponseOptional(
     base::expected<proto::LauncherAppResponse, QueryError> query_response) {
   if (query_response.has_value()) {
@@ -72,12 +62,13 @@
 
 }  // namespace
 
-void GetApps(const DeviceInfo& device_info,
-             scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-             GetAppsCallback callback) {
-  QueryAlmanacApi<proto::LauncherAppResponse>(
-      url_loader_factory, kTrafficAnnotation, BuildRequestBody(device_info),
-      kAlmanacLauncherAppEndpoint, kMaxResponseSizeInBytes,
+void GetApps(Profile* profile, GetAppsCallback callback) {
+  proto::LauncherAppRequest request_proto;
+
+  QueryAlmanacApiWithContext<proto::LauncherAppRequest,
+                             proto::LauncherAppResponse>(
+      profile, kAlmanacLauncherAppEndpoint, request_proto, kTrafficAnnotation,
+      kMaxResponseSizeInBytes,
       /*error_histogram_name=*/std::nullopt,
       base::BindOnce(&MakeResponseOptional).Then(std::move(callback)));
 }
diff --git a/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint.h b/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint.h
index 291a7646..31f54d7 100644
--- a/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint.h
+++ b/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint.h
@@ -12,29 +12,19 @@
 #include "chrome/browser/apps/app_discovery_service/almanac_api/launcher_app.pb.h"
 
 class GURL;
+class Profile;
 
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace apps {
-
-struct DeviceInfo;
-
-namespace launcher_app_almanac_endpoint {
+namespace apps::launcher_app_almanac_endpoint {
 
 using GetAppsCallback =
     base::OnceCallback<void(std::optional<proto::LauncherAppResponse>)>;
 
 // Fetches a list of apps from the Launcher App endpoint in the Almanac server.
-void GetApps(const DeviceInfo& device_info,
-             scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-             GetAppsCallback callback);
+void GetApps(Profile* profile, GetAppsCallback callback);
 
 // Returns the GURL for the endpoint. Exposed for tests.
 GURL GetServerUrl();
 
-}  // namespace launcher_app_almanac_endpoint
-}  // namespace apps
+}  // namespace apps::launcher_app_almanac_endpoint
 
 #endif  // CHROME_BROWSER_APPS_APP_DISCOVERY_SERVICE_LAUNCHER_APP_ALMANAC_ENDPOINT_H_
diff --git a/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint_unittest.cc b/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint_unittest.cc
index cc2bb7f..27eb325 100644
--- a/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint_unittest.cc
+++ b/chrome/browser/apps/app_discovery_service/launcher_app_almanac_endpoint_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/test/bind.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_request_headers.h"
@@ -25,43 +27,24 @@
 
 class LauncherAppAlmanacEndpointTest : public testing::Test {
  public:
-  LauncherAppAlmanacEndpointTest()
-      : test_shared_loader_factory_(
-            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &url_loader_factory_)) {}
+  void SetUp() override {
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetSharedURLLoaderFactory(
+        url_loader_factory_.GetSafeWeakWrapper());
+    profile_ = profile_builder.Build();
+  }
+
+  Profile* profile() { return profile_.get(); }
 
  protected:
   network::TestURLLoaderFactory url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-
-  DeviceInfo device_info_;
 
  private:
   content::BrowserTaskEnvironment task_environment_;
+  ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
+  std::unique_ptr<TestingProfile> profile_;
 };
 
-TEST_F(LauncherAppAlmanacEndpointTest, GetAppsRequest) {
-  std::string method;
-  std::optional<std::string> method_override_header;
-  std::optional<std::string> content_type;
-
-  url_loader_factory_.SetInterceptor(
-      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
-        content_type =
-            request.headers.GetHeader(net::HttpRequestHeaders::kContentType);
-        method_override_header =
-            request.headers.GetHeader("X-HTTP-Method-Override");
-        method = request.method;
-      }));
-
-  launcher_app_almanac_endpoint::GetApps(
-      device_info_, test_shared_loader_factory_, base::DoNothing());
-
-  EXPECT_EQ(method, "POST");
-  EXPECT_EQ(method_override_header, "GET");
-  EXPECT_EQ(content_type, "application/x-protobuf");
-}
-
 TEST_F(LauncherAppAlmanacEndpointTest, GetAppsSuccess) {
   proto::LauncherAppResponse response;
   response.add_app_groups();
@@ -72,8 +55,7 @@
 
   base::test::TestFuture<std::optional<proto::LauncherAppResponse>>
       observed_response;
-  launcher_app_almanac_endpoint::GetApps(device_info_,
-                                         test_shared_loader_factory_,
+  launcher_app_almanac_endpoint::GetApps(profile(),
                                          observed_response.GetCallback());
   ASSERT_TRUE(observed_response.Get().has_value());
   EXPECT_EQ(observed_response.Get()->app_groups_size(), 1);
@@ -83,8 +65,7 @@
   url_loader_factory_.AddResponse(
       launcher_app_almanac_endpoint::GetServerUrl().spec(), "");
   base::test::TestFuture<std::optional<proto::LauncherAppResponse>> response;
-  launcher_app_almanac_endpoint::GetApps(
-      device_info_, test_shared_loader_factory_, response.GetCallback());
+  launcher_app_almanac_endpoint::GetApps(profile(), response.GetCallback());
   ASSERT_TRUE(response.Get().has_value());
   EXPECT_EQ(response.Get()->app_groups_size(), 0);
 }
@@ -95,8 +76,7 @@
       /*content=*/"", net::HTTP_INTERNAL_SERVER_ERROR);
 
   base::test::TestFuture<std::optional<proto::LauncherAppResponse>> response;
-  launcher_app_almanac_endpoint::GetApps(
-      device_info_, test_shared_loader_factory_, response.GetCallback());
+  launcher_app_almanac_endpoint::GetApps(profile(), response.GetCallback());
   EXPECT_FALSE(response.Get().has_value());
 }
 
@@ -108,8 +88,7 @@
       network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
 
   base::test::TestFuture<std::optional<proto::LauncherAppResponse>> response;
-  launcher_app_almanac_endpoint::GetApps(
-      device_info_, test_shared_loader_factory_, response.GetCallback());
+  launcher_app_almanac_endpoint::GetApps(profile(), response.GetCallback());
   EXPECT_FALSE(response.Get().has_value());
 }
 
diff --git a/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.cc b/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.cc
index 266b119..7ca6be21 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.cc
@@ -11,10 +11,10 @@
 #include "base/functional/callback.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/apps/almanac_api_client/almanac_api_util.h"
-#include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
 #include "chrome/browser/apps/almanac_api_client/proto/client_context.pb.h"
 #include "chrome/browser/apps/app_preload_service/app_preload_service.h"
 #include "chrome/browser/apps/app_preload_service/proto/app_preload.pb.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/app_constants/constants.h"
 #include "net/base/net_errors.h"
@@ -65,14 +65,6 @@
       }
     )");
 
-std::string BuildGetAppsForFirstLoginRequestBody(const apps::DeviceInfo& info) {
-  apps::proto::AppPreloadListRequest request_proto;
-  *request_proto.mutable_device_context() = info.ToDeviceContext();
-  *request_proto.mutable_user_context() = info.ToUserContext();
-
-  return request_proto.SerializeAsString();
-}
-
 // Filters entries in LauncherConfig and ShelfConfig to ignore when the
 // specified feature is disabled.
 bool IsFeatureEnabled(const std::string& name) {
@@ -181,15 +173,13 @@
 
 }  // namespace
 
-void GetAppsForFirstLogin(
-    const DeviceInfo& device_info,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    GetInitialAppsCallback callback) {
-  QueryAlmanacApi<proto::AppPreloadListResponse>(
-      url_loader_factory, kTrafficAnnotation,
-      BuildGetAppsForFirstLoginRequestBody(device_info),
-      kAppPreloadAlmanacEndpoint, kMaxResponseSizeInBytes,
-      kServerErrorHistogramName,
+void GetAppsForFirstLogin(Profile* profile, GetInitialAppsCallback callback) {
+  proto::AppPreloadListRequest request_proto;
+
+  QueryAlmanacApiWithContext<proto::AppPreloadListRequest,
+                             proto::AppPreloadListResponse>(
+      profile, kAppPreloadAlmanacEndpoint, request_proto, kTrafficAnnotation,
+      kMaxResponseSizeInBytes, kServerErrorHistogramName,
       base::BindOnce(&ConvertAppPreloadListResponseProto, std::move(callback)));
 }
 
diff --git a/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.h b/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.h
index 5ddbeaf..0573aa4 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.h
+++ b/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.h
@@ -13,16 +13,9 @@
 #include "chrome/browser/apps/app_preload_service/preload_app_definition.h"
 
 class GURL;
+class Profile;
 
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace apps {
-
-struct DeviceInfo;
-
-namespace app_preload_almanac_endpoint {
+namespace apps::app_preload_almanac_endpoint {
 
 using GetInitialAppsCallback =
     base::OnceCallback<void(std::optional<std::vector<PreloadAppDefinition>>,
@@ -33,15 +26,11 @@
 // the App Provisioning Service API. `callback` will be called with a list of
 // (possibly zero) apps, or `std::nullopt` if an error occurred while
 // fetching apps.
-void GetAppsForFirstLogin(
-    const DeviceInfo& device_info,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    GetInitialAppsCallback callback);
+void GetAppsForFirstLogin(Profile* profile, GetInitialAppsCallback callback);
 
 // Gets the URL for the App Provisioning Service endpoint. Exposed for tests.
 GURL GetServerUrl();
 
-}  // namespace app_preload_almanac_endpoint
-}  // namespace apps
+}  // namespace apps::app_preload_almanac_endpoint
 
 #endif  // CHROME_BROWSER_APPS_APP_PRELOAD_SERVICE_APP_PRELOAD_ALMANAC_ENDPOINT_H_
diff --git a/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint_unittest.cc b/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint_unittest.cc
index 56af814..149e56b 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint_unittest.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint_unittest.cc
@@ -20,6 +20,8 @@
 #include "chrome/browser/apps/app_preload_service/app_preload_service.h"
 #include "chrome/browser/apps/app_preload_service/preload_app_definition.h"
 #include "chrome/browser/apps/app_preload_service/proto/app_preload.pb.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/http/http_request_headers.h"
 #include "services/network/public/cpp/resource_request.h"
@@ -34,56 +36,48 @@
 
 class AppPreloadAlmanacEndpointTest : public testing::Test {
  public:
-  AppPreloadAlmanacEndpointTest()
-      : test_shared_loader_factory_(
-            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &url_loader_factory_)) {
+  AppPreloadAlmanacEndpointTest() {
     feature_list_.InitAndDisableFeature(kAppPreloadServiceEnableTestApps);
   }
 
+  void SetUp() override {
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetSharedURLLoaderFactory(
+        url_loader_factory_.GetSafeWeakWrapper());
+    profile_ = profile_builder.Build();
+  }
+
+  Profile* profile() { return profile_.get(); }
+
  protected:
   network::TestURLLoaderFactory url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   base::HistogramTester histograms_;
   base::test::ScopedFeatureList feature_list_;
 
  private:
   content::BrowserTaskEnvironment task_environment_;
+  ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
+  std::unique_ptr<TestingProfile> profile_;
 };
 
 TEST_F(AppPreloadAlmanacEndpointTest, GetAppsForFirstLoginRequest) {
-  // We only set enough fields to verify that context protos are attached to the
-  // request.
-  DeviceInfo device_info;
-  device_info.board = "brya";
-  device_info.user_type = "unmanaged";
-
-  std::string method;
-  std::optional<std::string> method_override_header;
-  std::optional<std::string> content_type;
   std::string body;
 
+  base::RunLoop run_loop;
   url_loader_factory_.SetInterceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
-        content_type =
-            request.headers.GetHeader(net::HttpRequestHeaders::kContentType);
-        method_override_header =
-            request.headers.GetHeader("X-HTTP-Method-Override");
-        method = request.method;
         body = network::GetUploadData(request);
+        run_loop.Quit();
       }));
 
-  app_preload_almanac_endpoint::GetAppsForFirstLogin(
-      device_info, test_shared_loader_factory_, base::DoNothing());
-
-  EXPECT_EQ(method, "POST");
-  EXPECT_EQ(method_override_header, "GET");
-  EXPECT_EQ(content_type, "application/x-protobuf");
+  app_preload_almanac_endpoint::GetAppsForFirstLogin(profile(),
+                                                     base::DoNothing());
+  run_loop.Run();
 
   proto::AppPreloadListRequest request;
   ASSERT_TRUE(request.ParseFromString(body));
 
-  EXPECT_EQ(request.device_context().board(), "brya");
+  EXPECT_TRUE(request.has_device_context());
   EXPECT_EQ(request.user_context().user_type(),
             apps::proto::ClientUserContext::USERTYPE_UNMANAGED);
 }
@@ -163,7 +157,7 @@
                          LauncherOrdering, ShelfPinOrdering>
       test_callback;
   app_preload_almanac_endpoint::GetAppsForFirstLogin(
-      DeviceInfo(), test_shared_loader_factory_, test_callback.GetCallback());
+      profile(), test_callback.GetCallback());
 
   auto apps = std::get<0>(test_callback.Get());
   EXPECT_TRUE(apps.has_value());
@@ -203,8 +197,8 @@
   base::test::TestFuture<std::optional<std::vector<PreloadAppDefinition>>,
                          LauncherOrdering, ShelfPinOrdering>
       result;
-  app_preload_almanac_endpoint::GetAppsForFirstLogin(
-      DeviceInfo(), test_shared_loader_factory_, result.GetCallback());
+  app_preload_almanac_endpoint::GetAppsForFirstLogin(profile(),
+                                                     result.GetCallback());
   EXPECT_FALSE(std::get<0>(result.Get()).has_value());
 }
 
@@ -217,8 +211,8 @@
   base::test::TestFuture<std::optional<std::vector<PreloadAppDefinition>>,
                          LauncherOrdering, ShelfPinOrdering>
       result;
-  app_preload_almanac_endpoint::GetAppsForFirstLogin(
-      DeviceInfo(), test_shared_loader_factory_, result.GetCallback());
+  app_preload_almanac_endpoint::GetAppsForFirstLogin(profile(),
+                                                     result.GetCallback());
   EXPECT_FALSE(std::get<0>(result.Get()).has_value());
 }
 
diff --git a/chrome/browser/apps/app_preload_service/app_preload_service.cc b/chrome/browser/apps/app_preload_service/app_preload_service.cc
index 5abdfbd5..7f872d5 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_service.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_service.cc
@@ -18,7 +18,6 @@
 #include "base/ranges/algorithm.h"
 #include "base/strings/strcat.h"
 #include "base/time/time.h"
-#include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
 #include "chrome/browser/apps/app_preload_service/app_preload_almanac_endpoint.h"
 #include "chrome/browser/apps/app_preload_service/app_preload_service_factory.h"
 #include "chrome/browser/apps/app_preload_service/preload_app_definition.h"
@@ -94,9 +93,7 @@
              "AppPreloadServiceAllUserTypes",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-AppPreloadService::AppPreloadService(Profile* profile)
-    : profile_(profile),
-      device_info_manager_(std::make_unique<DeviceInfoManager>(profile)) {
+AppPreloadService::AppPreloadService(Profile* profile) : profile_(profile) {
   if (g_disable_preloads_on_startup_for_testing_) {
     return;
   }
@@ -164,17 +161,14 @@
 
   if ((first_run_started && !first_run_complete) ||
       base::FeatureList::IsEnabled(kAppPreloadServiceForceRun)) {
-    device_info_manager_->GetDeviceInfo(
-        base::BindOnce(&AppPreloadService::StartAppInstallationForFirstLogin,
-                       weak_ptr_factory_.GetWeakPtr(), start_time));
+    StartAppInstallationForFirstLogin(start_time);
   } else {
     data_ready_ = true;
   }
 }
 
 void AppPreloadService::StartAppInstallationForFirstLogin(
-    base::TimeTicks start_time,
-    DeviceInfo device_info) {
+    base::TimeTicks start_time) {
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
       profile_->GetURLLoaderFactory();
   if (!url_loader_factory.get()) {
@@ -186,7 +180,7 @@
     return;
   }
   app_preload_almanac_endpoint::GetAppsForFirstLogin(
-      device_info, url_loader_factory,
+      profile_,
       base::BindOnce(&AppPreloadService::OnGetAppsForFirstLoginCompleted,
                      weak_ptr_factory_.GetWeakPtr(), start_time));
 }
diff --git a/chrome/browser/apps/app_preload_service/app_preload_service.h b/chrome/browser/apps/app_preload_service/app_preload_service.h
index c2ef8b86..d9edfbde 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_service.h
+++ b/chrome/browser/apps/app_preload_service/app_preload_service.h
@@ -31,11 +31,8 @@
 
 namespace apps {
 
-class DeviceInfoManager;
 class PreloadAppDefinition;
 
-struct DeviceInfo;
-
 // Debugging feature to always run the App Preload Service on startup, even if
 // the Profile would not normally be eligible.
 BASE_DECLARE_FEATURE(kAppPreloadServiceForceRun);
@@ -105,8 +102,7 @@
   // service, processes the list and installs the app list. This call should
   // only be used the first time a profile is created on the device as this call
   // installs a set of default and OEM apps.
-  void StartAppInstallationForFirstLogin(base::TimeTicks start_time,
-                                         DeviceInfo device_info);
+  void StartAppInstallationForFirstLogin(base::TimeTicks start_time);
   // Processes the list of apps retrieved by the server connector.
   void OnGetAppsForFirstLoginCompleted(
       base::TimeTicks start_time,
@@ -125,7 +121,6 @@
   const base::Value::Dict& GetStateManager() const;
 
   raw_ptr<Profile> profile_;
-  std::unique_ptr<DeviceInfoManager> device_info_manager_;
   // Set true when response is received, or if APS is complete and not running.
   bool data_ready_ = false;
   std::vector<PackageId> pin_apps_;
diff --git a/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint.cc b/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint.cc
index b11bf90e..fb9fb46 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint.cc
@@ -57,16 +57,6 @@
 
 constexpr int kMaxResponseSizeInBytes = 1024 * 1024;
 
-std::string BuildRequestBody(const DeviceInfo& info,
-                             std::string serialized_package_id) {
-  proto::AppInstallRequest request_proto;
-  *request_proto.mutable_device_context() = info.ToDeviceContext();
-  *request_proto.mutable_user_context() = info.ToUserContext();
-  *request_proto.mutable_package_id() = std::move(serialized_package_id);
-
-  return request_proto.SerializeAsString();
-}
-
 std::optional<AppInstallData> ParseAppInstallResponseProto(
     const proto::AppInstallResponse& app_install_response) {
   if (!app_install_response.has_app_instance()) {
@@ -188,15 +178,16 @@
   return GetAlmanacEndpointUrl(kAlmanacAppInstallEndpoint);
 }
 
-void GetAppInstallInfo(
-    PackageId package_id,
-    DeviceInfo device_info,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    GetAppInstallInfoCallback callback) {
-  QueryAlmanacApi<proto::AppInstallResponse>(
-      url_loader_factory, kTrafficAnnotation,
-      BuildRequestBody(device_info, package_id.ToString()),
-      kAlmanacAppInstallEndpoint, kMaxResponseSizeInBytes,
+void GetAppInstallInfo(Profile* profile,
+                       PackageId package_id,
+                       GetAppInstallInfoCallback callback) {
+  proto::AppInstallRequest request;
+  request.set_package_id(package_id.ToString());
+
+  QueryAlmanacApiWithContext<proto::AppInstallRequest,
+                             proto::AppInstallResponse>(
+      profile, kAlmanacAppInstallEndpoint, std::move(request),
+      kTrafficAnnotation, kMaxResponseSizeInBytes,
       /*error_histogram_name=*/std::nullopt,
       base::BindOnce(&ConvertAppInstallResponseProto)
           .Then(std::move(callback)));
@@ -204,15 +195,16 @@
 
 using GetAppInstallUrlCallback =
     base::OnceCallback<void(base::expected<GURL, QueryError>)>;
-void GetAppInstallUrl(
-    std::string serialized_package_id,
-    DeviceInfo device_info,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    GetAppInstallUrlCallback callback) {
-  QueryAlmanacApi<proto::AppInstallResponse>(
-      url_loader_factory, kTrafficAnnotation,
-      BuildRequestBody(device_info, std::move(serialized_package_id)),
-      kAlmanacAppInstallEndpoint, kMaxResponseSizeInBytes,
+void GetAppInstallUrl(Profile* profile,
+                      std::string serialized_package_id,
+                      GetAppInstallUrlCallback callback) {
+  proto::AppInstallRequest request;
+  request.set_package_id(serialized_package_id);
+
+  QueryAlmanacApiWithContext<proto::AppInstallRequest,
+                             proto::AppInstallResponse>(
+      profile, kAlmanacAppInstallEndpoint, std::move(request),
+      kTrafficAnnotation, kMaxResponseSizeInBytes,
       /*error_histogram_name=*/std::nullopt,
       base::BindOnce(&ExtractAppInstallUrlFromResponseProto)
           .Then(std::move(callback)));
diff --git a/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint.h b/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint.h
index 848a1ca..8da3930 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint.h
+++ b/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint.h
@@ -13,15 +13,10 @@
 
 class GURL;
 
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
 namespace apps {
 
 class PackageId;
 struct AppInstallData;
-struct DeviceInfo;
 
 namespace app_install_almanac_endpoint {
 
@@ -29,21 +24,17 @@
 // server.
 using GetAppInstallInfoCallback =
     base::OnceCallback<void(base::expected<AppInstallData, QueryError>)>;
-void GetAppInstallInfo(
-    PackageId package_id,
-    DeviceInfo device_info,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    GetAppInstallInfoCallback callback);
+void GetAppInstallInfo(Profile* profile,
+                       PackageId package_id,
+                       GetAppInstallInfoCallback callback);
 
 // Fetches the app install URL from the app install endpoint of the Almanac
 // server.
 using GetAppInstallUrlCallback =
     base::OnceCallback<void(base::expected<GURL, QueryError>)>;
-void GetAppInstallUrl(
-    std::string serialized_package_id,
-    DeviceInfo device_info,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    GetAppInstallUrlCallback callback);
+void GetAppInstallUrl(Profile* profile,
+                      std::string serialized_package_id,
+                      GetAppInstallUrlCallback callback);
 
 GURL GetEndpointUrlForTesting();
 
diff --git a/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint_unittest.cc b/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint_unittest.cc
index cc3a6e7..da184cc 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint_unittest.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_almanac_endpoint_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
 #include "base/strings/to_string.h"
 #include "base/test/bind.h"
 #include "base/test/test_future.h"
@@ -17,6 +18,8 @@
 #include "chrome/browser/apps/almanac_api_client/proto/client_context.pb.h"
 #include "chrome/browser/apps/app_service/app_install/app_install.pb.h"
 #include "chrome/browser/apps/app_service/app_install/app_install_types.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/http/http_request_headers.h"
@@ -42,45 +45,43 @@
  public:
   AppInstallAlmanacEndpointTest() = default;
 
+  void SetUp() override {
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetSharedURLLoaderFactory(
+        test_url_loader_factory_.GetSafeWeakWrapper());
+    profile_ = profile_builder.Build();
+  }
+
+  Profile* profile() { return profile_.get(); }
+
  protected:
   network::TestURLLoaderFactory test_url_loader_factory_;
 
  private:
   content::BrowserTaskEnvironment task_environment_;
+  ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
+  std::unique_ptr<TestingProfile> profile_;
 };
 
 TEST_F(AppInstallAlmanacEndpointTest, GetAppInstallInfoRequest) {
-  DeviceInfo device_info;
-  device_info.board = "brya";
-  device_info.user_type = "unmanaged";
-
-  std::string method;
-  std::optional<std::string> method_override_header;
-  std::optional<std::string> content_type;
   std::string body;
 
+  base::RunLoop run_loop;
   test_url_loader_factory_.SetInterceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
-        content_type =
-            request.headers.GetHeader(net::HttpRequestHeaders::kContentType);
-        method_override_header =
-            request.headers.GetHeader("X-HTTP-Method-Override");
-        method = request.method;
         body = network::GetUploadData(request);
+        run_loop.Quit();
       }));
 
   app_install_almanac_endpoint::GetAppInstallInfo(
-      PackageId(PackageType::kWeb, "https://example.com/"), device_info,
-      test_url_loader_factory_.GetSafeWeakWrapper(), base::DoNothing());
-
-  EXPECT_EQ(method, "POST");
-  EXPECT_EQ(method_override_header, "GET");
-  EXPECT_EQ(content_type, "application/x-protobuf");
+      profile(), PackageId(PackageType::kWeb, "https://example.com/"),
+      base::DoNothing());
+  run_loop.Run();
 
   proto::AppInstallRequest request;
   ASSERT_TRUE(request.ParseFromString(body));
 
-  EXPECT_EQ(request.device_context().board(), "brya");
+  EXPECT_TRUE(request.has_device_context());
   EXPECT_EQ(request.user_context().user_type(),
             apps::proto::ClientUserContext::USERTYPE_UNMANAGED);
   EXPECT_EQ(request.package_id(), "web:https://example.com/");
@@ -131,9 +132,7 @@
 
   ResponseFuture response_future;
   app_install_almanac_endpoint::GetAppInstallInfo(
-      kTestPackageId, DeviceInfo(),
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      response_future.GetCallback());
+      profile(), kTestPackageId, response_future.GetCallback());
   EXPECT_TRUE(response_future.Get().has_value());
 
   AppInstallData expected_data(
@@ -184,9 +183,7 @@
 
   ResponseFuture response_future;
   app_install_almanac_endpoint::GetAppInstallInfo(
-      kTestPackageId, DeviceInfo(),
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      response_future.GetCallback());
+      profile(), kTestPackageId, response_future.GetCallback());
 
   AppInstallData expected_data(PackageId(PackageType::kArc, "com.foo.app"));
   expected_data.name = "Example";
@@ -207,9 +204,7 @@
 
   ResponseFuture response_future;
   app_install_almanac_endpoint::GetAppInstallInfo(
-      kTestPackageId, DeviceInfo(),
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      response_future.GetCallback());
+      profile(), kTestPackageId, response_future.GetCallback());
   EXPECT_EQ(response_future.Get().error().type, QueryError::kBadResponse);
 }
 
@@ -220,9 +215,7 @@
 
   ResponseFuture response_future;
   app_install_almanac_endpoint::GetAppInstallInfo(
-      kTestPackageId, DeviceInfo(),
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      response_future.GetCallback());
+      profile(), kTestPackageId, response_future.GetCallback());
   EXPECT_EQ(response_future.Get().error().type, QueryError::kBadResponse);
 }
 
@@ -241,8 +234,7 @@
 
   ResponseFuture response_future;
   app_install_almanac_endpoint::GetAppInstallInfo(
-      PackageId(PackageType::kWeb, "https://example.com/"), DeviceInfo(),
-      test_url_loader_factory_.GetSafeWeakWrapper(),
+      profile(), PackageId(PackageType::kWeb, "https://example.com/"),
       response_future.GetCallback());
   EXPECT_EQ(response_future.Get().error().type, QueryError::kBadResponse);
 }
@@ -254,9 +246,7 @@
 
   ResponseFuture response_future;
   app_install_almanac_endpoint::GetAppInstallInfo(
-      kTestPackageId, DeviceInfo(),
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      response_future.GetCallback());
+      profile(), kTestPackageId, response_future.GetCallback());
   EXPECT_EQ(response_future.Get().error().type, QueryError::kConnectionError);
 }
 
@@ -268,9 +258,7 @@
 
   ResponseFuture response_future;
   app_install_almanac_endpoint::GetAppInstallInfo(
-      kTestPackageId, DeviceInfo(),
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      response_future.GetCallback());
+      profile(), kTestPackageId, response_future.GetCallback());
   EXPECT_EQ(response_future.Get().error().type, QueryError::kConnectionError);
 }
 
@@ -283,9 +271,7 @@
 
   ResponseFuture response_future;
   app_install_almanac_endpoint::GetAppInstallInfo(
-      kTestPackageId, DeviceInfo(),
-      test_url_loader_factory_.GetSafeWeakWrapper(),
-      response_future.GetCallback());
+      profile(), kTestPackageId, response_future.GetCallback());
   EXPECT_EQ(response_future.Get().error().type, QueryError::kBadRequest);
 }
 
diff --git a/chrome/browser/apps/app_service/app_install/app_install_service_ash.cc b/chrome/browser/apps/app_service/app_install/app_install_service_ash.cc
index c3054de..0375b01 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_service_ash.cc
+++ b/chrome/browser/apps/app_service/app_install/app_install_service_ash.cc
@@ -88,7 +88,6 @@
 
 AppInstallServiceAsh::AppInstallServiceAsh(Profile& profile)
     : profile_(profile),
-      device_info_manager_(&*profile_),
       arc_app_installer_(&*profile_),
       web_app_installer_(&*profile_) {}
 
@@ -231,19 +230,8 @@
 void AppInstallServiceAsh::FetchAppInstallData(
     PackageId package_id,
     app_install_almanac_endpoint::GetAppInstallInfoCallback data_callback) {
-  device_info_manager_.GetDeviceInfo(
-      base::BindOnce(&AppInstallServiceAsh::FetchAppInstallDataWithDeviceInfo,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(package_id),
-                     std::move(data_callback)));
-}
-
-void AppInstallServiceAsh::FetchAppInstallDataWithDeviceInfo(
-    PackageId package_id,
-    app_install_almanac_endpoint::GetAppInstallInfoCallback data_callback,
-    DeviceInfo device_info) {
-  app_install_almanac_endpoint::GetAppInstallInfo(
-      package_id, std::move(device_info), profile_->GetURLLoaderFactory(),
-      std::move(data_callback));
+  app_install_almanac_endpoint::GetAppInstallInfo(&profile_.get(), package_id,
+                                                  std::move(data_callback));
 }
 
 void AppInstallServiceAsh::PerformInstallHeadless(
@@ -410,19 +398,8 @@
 void AppInstallServiceAsh::FetchAppInstallUrl(
     std::string serialized_package_id,
     base::OnceCallback<void(base::expected<GURL, QueryError>)> callback) {
-  device_info_manager_.GetDeviceInfo(
-      base::BindOnce(&AppInstallServiceAsh::FetchAppInstallUrlWithDeviceInfo,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     std::move(serialized_package_id), std::move(callback)));
-}
-
-void AppInstallServiceAsh::FetchAppInstallUrlWithDeviceInfo(
-    std::string serialized_package_id,
-    base::OnceCallback<void(base::expected<GURL, QueryError>)> callback,
-    DeviceInfo device_info) {
   app_install_almanac_endpoint::GetAppInstallUrl(
-      serialized_package_id, std::move(device_info),
-      profile_->GetURLLoaderFactory(), std::move(callback));
+      &profile_.get(), serialized_package_id, std::move(callback));
 }
 
 void AppInstallServiceAsh::MaybeLaunchAppInstallUrl(
diff --git a/chrome/browser/apps/app_service/app_install/app_install_service_ash.h b/chrome/browser/apps/app_service/app_install/app_install_service_ash.h
index 9f55697e..3d77ec7 100644
--- a/chrome/browser/apps/app_service/app_install/app_install_service_ash.h
+++ b/chrome/browser/apps/app_service/app_install/app_install_service_ash.h
@@ -84,10 +84,6 @@
   void FetchAppInstallData(
       PackageId package_id,
       app_install_almanac_endpoint::GetAppInstallInfoCallback data_callback);
-  void FetchAppInstallDataWithDeviceInfo(
-      PackageId package_id,
-      app_install_almanac_endpoint::GetAppInstallInfoCallback data_callback,
-      DeviceInfo device_info);
 
   void PerformInstallHeadless(AppInstallSurface surface,
                               PackageId expected_package_id,
@@ -124,16 +120,11 @@
   void FetchAppInstallUrl(
       std::string serialized_package_id,
       base::OnceCallback<void(base::expected<GURL, QueryError>)> callback);
-  void FetchAppInstallUrlWithDeviceInfo(
-      std::string serialized_package_id,
-      base::OnceCallback<void(base::expected<GURL, QueryError>)> callback,
-      DeviceInfo device_info);
   void MaybeLaunchAppInstallUrl(
       base::OnceCallback<void(AppInstallResult)> callback,
       base::expected<GURL, QueryError> install_url);
 
   raw_ref<Profile> profile_;
-  DeviceInfoManager device_info_manager_;
   ArcAppInstaller arc_app_installer_;
   WebAppInstaller web_app_installer_;
 
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 803d9d5..038e0823 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -44,6 +44,8 @@
 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
+#include "chrome/browser/serial/serial_chooser_context.h"
+#include "chrome/browser/serial/serial_chooser_context_factory.h"
 #include "chrome/browser/task_manager/task_manager_browsertest_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -136,9 +138,11 @@
 #include "pdf/buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "services/device/public/cpp/test/fake_hid_manager.h"
+#include "services/device/public/cpp/test/fake_serial_port_manager.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
 #include "services/device/public/mojom/hid.mojom.h"
+#include "services/device/public/mojom/serial.mojom.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
@@ -7118,3 +7122,59 @@
   // available for that context.
   TestHelper("testCannotRequestFonts", "web_view/shim", NO_TEST_SERVER);
 }
+
+class WebViewSerialTest : public WebViewTest {
+ public:
+  void SetUpOnMainThread() override {
+    WebViewTest::SetUpOnMainThread();
+    mojo::PendingRemote<device::mojom::SerialPortManager> port_manager;
+    port_manager_.AddReceiver(port_manager.InitWithNewPipeAndPassReceiver());
+    context()->SetPortManagerForTesting(std::move(port_manager));
+  }
+
+  void TearDownOnMainThread() override { WebViewTest::TearDownOnMainThread(); }
+
+  device::FakeSerialPortManager& port_manager() { return port_manager_; }
+  SerialChooserContext* context() {
+    return SerialChooserContextFactory::GetForProfile(browser()->profile());
+  }
+
+  void CreatePortAndGrantPermissionToOrigin(const url::Origin& origin) {
+    // Create port and grant permission to it.
+    auto port = device::mojom::SerialPortInfo::New();
+    port->token = base::UnguessableToken::Create();
+    context()->GrantPortPermission(origin, *port);
+    port_manager().AddPort(std::move(port));
+  }
+
+ private:
+  device::FakeSerialPortManager port_manager_;
+};
+
+IN_PROC_BROWSER_TEST_F(WebViewSerialTest,
+                       Shim_TestEnabledInTabButNotInWebView) {
+  // We start the test server here, instead of in TestHelper, because we need
+  // to know the origin used in both the tab and webview.
+  ASSERT_TRUE(StartEmbeddedTestServer());
+
+  const GURL url = embedded_test_server()->GetURL("localhost", "/title1.html");
+  url::Origin origin = url::Origin::Create(url);
+  CreatePortAndGrantPermissionToOrigin(origin);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+  content::WebContents* tab_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Test that serial works in a tab.
+  EXPECT_EQ(1, EvalJs(tab_web_contents,
+                      R"(
+(async () => {
+  const ports = await navigator.serial.getPorts().then(ports => ports.length);
+  return ports;
+})();
+      )"));
+
+  // Have the embedder create a webview which navigates to the same origin and
+  // attempts to use serial.
+  TestHelper("testSerialDisabled", "web_view/shim", NO_TEST_SERVER);
+}
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index d0dba67..e8aea48 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -27,12 +27,6 @@
     "browser_context_keyed_service_factories.h",
     "chrome_browser_main_parts_ash.cc",
     "chrome_browser_main_parts_ash.h",
-    "system_token_cert_db_initializer.cc",
-    "system_token_cert_db_initializer.h",
-    "tpm_firmware_update.cc",
-    "tpm_firmware_update.h",
-    "tpm_firmware_update_notification.cc",
-    "tpm_firmware_update_notification.h",
   ]
 
   allow_circular_includes_from = [
@@ -45,7 +39,6 @@
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/screens/chromevox_hint",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/settings",
@@ -54,6 +47,7 @@
     "//chrome/browser/media/webrtc",
     "//chrome/browser/policy:onc",
     "//chrome/browser/sync",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/views/toolbar",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/web_applications",
@@ -302,8 +296,6 @@
     "//chromeos/dbus/missive",
     "//chromeos/dbus/missive:history_tracker",
     "//chromeos/dbus/power:power_manager_proto",
-    "//chromeos/dbus/tpm_manager",
-    "//chromeos/dbus/tpm_manager:tpm_manager_proto",
     "//chromeos/printing",
     "//chromeos/services/machine_learning/public/mojom",
     "//chromeos/services/network_config/public/cpp",
@@ -575,6 +567,7 @@
     "//chrome/browser/ash/calendar",
     "//chrome/browser/ash/camera",
     "//chrome/browser/ash/cert_provisioning",
+    "//chrome/browser/ash/certs",
     "//chrome/browser/ash/child_accounts",
     "//chrome/browser/ash/child_accounts/on_device_controls",
     "//chrome/browser/ash/concierge_helper",
@@ -622,7 +615,6 @@
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/signin",
     "//chrome/browser/ash/login/smart_lock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users/avatar",
     "//chrome/browser/ash/mahi",
     "//chrome/browser/ash/mahi/media_app",
@@ -712,6 +704,7 @@
     "//chrome/browser/ui/ash/global_media_controls",
     "//chrome/browser/ui/ash/holding_space",
     "//chrome/browser/ui/ash/keyboard",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/session",
     "//chrome/browser/ui/views/toolbar",
     "//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings_shared",
@@ -797,7 +790,6 @@
     "//chromeos/ash/components/standalone_browser",
     "//chromeos/ash/components/sync_wifi",
     "//chromeos/ash/components/tpm",
-    "//chromeos/ash/components/tpm:buildflags",
     "//chromeos/ash/resources:resources_grit",
     "//chromeos/ash/services/assistant:lib",
     "//chromeos/ash/services/assistant/public/cpp",
@@ -1061,8 +1053,6 @@
     "../ui/browser_finder_chromeos_unittest.cc",
     "../ui/webui/settings/about_handler_unittest.cc",
     "proxy_config_service_impl_unittest.cc",
-    "system_token_cert_db_initializer_unittest.cc",
-    "tpm_firmware_update_unittest.cc",
   ]
 
   deps = [
@@ -1175,7 +1165,6 @@
     "//chrome/browser/ash/login/demo_mode:test_support",
     "//chrome/browser/ash/login/lock/online_reauth",
     "//chrome/browser/ash/login/saml:test_support",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/multidevice_setup",
     "//chrome/browser/ash/net",
@@ -1241,6 +1230,7 @@
     "//chrome/browser/ui:test_support",
     "//chrome/browser/ui/ash/holding_space:test_support",
     "//chrome/browser/ui/ash/keyboard:ash_test_support",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/webui/ash/settings",
     "//chrome/browser/ui/webui/ash/settings/calculator:test_support",
@@ -1361,7 +1351,6 @@
     "//chromeos/ash/components/system_info",
     "//chromeos/ash/components/tether",
     "//chromeos/ash/components/tether:test_support",
-    "//chromeos/ash/components/tpm",
     "//chromeos/ash/components/trash_service",
     "//chromeos/ash/components/trash_service/public/cpp",
     "//chromeos/ash/components/trash_service/public/mojom",
@@ -1405,7 +1394,6 @@
     "//chromeos/dbus/power:power_manager_proto",
     "//chromeos/dbus/regmon",
     "//chromeos/dbus/regmon:regmon_proto",
-    "//chromeos/dbus/tpm_manager",
     "//chromeos/printing",
     "//chromeos/services/machine_learning/public/cpp",
     "//chromeos/services/machine_learning/public/cpp:stub",
@@ -1700,6 +1688,7 @@
     "//chrome/browser/ash/camera:unit_tests",
     "//chrome/browser/ash/camera_mic:unit_tests",
     "//chrome/browser/ash/cert_provisioning:unit_tests",
+    "//chrome/browser/ash/certs:unit_tests",
     "//chrome/browser/ash/child_accounts:unit_tests",
     "//chrome/browser/ash/concierge_helper:unit_tests",
     "//chrome/browser/ash/crosapi:unit_tests",
@@ -1818,6 +1807,7 @@
     "//chrome/browser/ash/system_logs:unit_tests",
     "//chrome/browser/ash/system_web_apps:unit_tests",
     "//chrome/browser/ash/tether:unit_tests",
+    "//chrome/browser/ash/tpm:unit_tests",
     "//chrome/browser/ash/trusted_vault:unit_tests",
     "//chrome/browser/ash/usb:unit_tests",
     "//chrome/browser/ash/video_conference:unit_tests",
@@ -1837,54 +1827,10 @@
   ]
 }
 
-source_set("browser_tests") {
+group("browser_tests") {
   testonly = true
 
-  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-
-  sources = [
-    "first_web_contents_profiler_ash_browsertest.cc",
-    "wmp_browsertest.cc",
-  ]
-
   deps = [
-    ":ash",
-    "//ash/constants",
-    "//ash/public/cpp",
-    "//base",
-    "//base/test:test_support",
-    "//chrome/browser/ash/app_restore",
-    "//chrome/browser/ash/app_restore:test_support",
-    "//chrome/browser/ash/input_method",
-    "//chrome/browser/ash/login:test_support",
-    "//chrome/browser/ash/login/lock",
-    "//chrome/browser/ash/login/lock:test_support",
-    "//chrome/browser/ash/login/saml:test_support",
-    "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
-    "//chrome/browser/ash/policy/core:test_support",
-    "//chrome/browser/ash/settings",
-    "//chrome/browser/ash/system_web_apps/apps",
-    "//chrome/browser/ash/system_web_apps/test_support:test_support_ui",
-    "//chrome/browser/ui",
-    "//chrome/browser/web_applications:web_applications_test_support",
-    "//chrome/common",
-    "//chrome/test:test_support",
-    "//chromeos/ash/components/dbus/shill",
-    "//components/account_id",
-    "//components/language/core/browser",
-    "//components/session_manager/core",
-    "//content/public/browser",
-    "//content/public/common",
-    "//content/test:test_support",
-    "//mojo/public/cpp/bindings",
-    "//net",
-    "//services/network/public/mojom",
-    "//testing/gtest",
-    "//ui/events:test_support",
-    "//url",
-
     # Gather browser tests from subdirectories.
     "//chrome/browser/ash/accessibility:browser_tests",
     "//chrome/browser/ash/account_manager:browser_tests",
diff --git a/chrome/browser/ash/accessibility/BUILD.gn b/chrome/browser/ash/accessibility/BUILD.gn
index 91593f4b..75a6d74 100644
--- a/chrome/browser/ash/accessibility/BUILD.gn
+++ b/chrome/browser/ash/accessibility/BUILD.gn
@@ -201,9 +201,9 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/preferences",
     "//chrome/browser/ui/ash/input_method",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/shelf",
     "//chrome/browser/ui/aura/accessibility",
     "//chrome/test:test_support",
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc
index 4d84faa..111d534 100644
--- a/chrome/browser/ash/accessibility/dictation_browsertest.cc
+++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -1025,7 +1025,9 @@
   SendFinalResultAndWaitForEditableValue(" Delete", "Veg");
   SendFinalResultAndWaitForEditableValue("delete", "Ve");
   SendFinalResultAndWaitForEditableValue("  delete ", "V");
-  SendFinalResultAndWaitForEditableValue("DELETE", "");
+  SendFinalResultAndWaitForEditableValue(
+      "DELETE",
+      (editable_type() == EditableType::kContentEditable) ? "\n" : "");
 }
 
 IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, MoveByCharacter) {
@@ -1066,7 +1068,9 @@
   std::string first_text = "Vega is the brightest star in Lyra";
   SendFinalResultAndWaitForEditableValue(first_text, first_text);
   SendFinalResultAndWaitForSelection("Select all", 0, first_text.size());
-  SendFinalResultAndWaitForEditableValue("delete", "");
+  SendFinalResultAndWaitForEditableValue(
+      "delete",
+      (editable_type() == EditableType::kContentEditable) ? "\n" : "");
   std::string second_text = "Vega is the fifth brightest star in the sky";
   SendFinalResultAndWaitForEditableValue(second_text, second_text);
   SendFinalResultAndWaitForSelection("Select all", 0, second_text.size());
@@ -1086,7 +1090,8 @@
   SendFinalResultAndWaitForSelection("select ALL ", 0, 8);
   SendFinalResultAndWaitForClipboardChanged("cut");
   EXPECT_EQ("StarStar", GetClipboardText());
-  WaitForEditableValue("");
+  WaitForEditableValue(
+      (editable_type() == EditableType::kContentEditable) ? "\n" : "");
   SendFinalResultAndWaitForEditableValue("  PaStE ", "StarStar");
 }
 
@@ -1148,7 +1153,9 @@
 
 IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevSentSimple) {
   SendFinalResultAndWaitForEditableValue("Hello, world.", "Hello, world.");
-  SendFinalResultAndWaitForEditableValue("delete the previous sentence", "");
+  SendFinalResultAndWaitForEditableValue(
+      "delete the previous sentence",
+      (editable_type() == EditableType::kContentEditable) ? "\n" : "");
 }
 
 IN_PROC_BROWSER_TEST_P(DictationRegexCommandsTest, DeletePrevSentWhiteSpace) {
@@ -1686,7 +1693,8 @@
   SendFinalResultAndWaitForSelection("highlight everything", 0, 8);
   SendFinalResultAndWaitForClipboardChanged("cut highlighted text");
   EXPECT_EQ("StarStar", GetClipboardText());
-  WaitForEditableValue("");
+  WaitForEditableValue(
+      (editable_type() == EditableType::kContentEditable) ? "\n" : "");
   SendFinalResultAndWaitForEditableValue("paste the copied text", "StarStar");
 }
 
@@ -1707,7 +1715,9 @@
 
 IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, DeletePrevSent) {
   SendFinalResultAndWaitForEditableValue("Hello, world.", "Hello, world.");
-  SendFinalResultAndWaitForEditableValue("erase sentence", "");
+  SendFinalResultAndWaitForEditableValue(
+      "erase sentence",
+      (editable_type() == EditableType::kContentEditable) ? "\n" : "");
 }
 
 IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, MoveByWord) {
@@ -1760,7 +1770,8 @@
 
 IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, DeleteAllText) {
   SendFinalResultAndWaitForEditableValue("Hello, world.", "Hello, world.");
-  SendFinalResultAndWaitForEditableValue("clear", "");
+  SendFinalResultAndWaitForEditableValue(
+      "clear", (editable_type() == EditableType::kContentEditable) ? "\n" : "");
 }
 
 IN_PROC_BROWSER_TEST_P(DictationPumpkinTest, NavStartText) {
@@ -1912,7 +1923,9 @@
         /*icon=*/DictationBubbleIconType::kMacroFail,
         /*text=*/message,
         /*hints=*/std::optional<std::vector<std::u16string>>());
-    SendFinalResultAndWaitForEditableValue("delete all", "");
+    SendFinalResultAndWaitForEditableValue(
+        "delete all",
+        (editable_type() == EditableType::kContentEditable) ? "\n" : "");
   }
 
  private:
diff --git a/chrome/browser/ash/accessibility/facegaze_test_utils.cc b/chrome/browser/ash/accessibility/facegaze_test_utils.cc
index 3b1231f..7097323 100644
--- a/chrome/browser/ash/accessibility/facegaze_test_utils.cc
+++ b/chrome/browser/ash/accessibility/facegaze_test_utils.cc
@@ -152,6 +152,8 @@
   cursor_location_ = gfx::Point(600, 400);
   buffer_size_ = 1;
   use_cursor_acceleration_ = false;
+  use_landmark_weights_ = false;
+  use_velocity_threshold_ = false;
   dialog_accepted_ = true;
 
   return *this;
@@ -210,6 +212,18 @@
   return *this;
 }
 
+FaceGazeTestUtils::Config& FaceGazeTestUtils::Config::WithLandmarkWeights(
+    bool use_weights) {
+  use_landmark_weights_ = use_weights;
+  return *this;
+}
+
+FaceGazeTestUtils::Config& FaceGazeTestUtils::Config::WithVelocityThreshold(
+    bool use_threshold) {
+  use_velocity_threshold_ = use_threshold;
+  return *this;
+}
+
 FaceGazeTestUtils::MockFaceLandmarkerResult::MockFaceLandmarkerResult() =
     default;
 FaceGazeTestUtils::MockFaceLandmarkerResult::~MockFaceLandmarkerResult() =
@@ -409,6 +423,8 @@
   // Set required configuration properties.
   SetBufferSize(config.buffer_size());
   SetCursorAcceleration(config.use_cursor_acceleration());
+  SetLandmarkWeights(config.use_landmark_weights());
+  SetVelocityThreshold(config.use_velocity_threshold());
 
   // By default the cursor is placed at the center of the screen. To
   // initialize FaceGaze, move the cursor somewhere, then move it to the
@@ -453,6 +469,20 @@
   GetPrefs()->CommitPendingWrite();
 }
 
+void FaceGazeTestUtils::SetLandmarkWeights(bool use_weights) {
+  std::string true_script = "faceGazeTestSupport.setLandmarkWeights(true);";
+  std::string false_script = "faceGazeTestSupport.setLandmarkWeights(false);";
+  ExecuteAccessibilityCommonScript(use_weights ? true_script : false_script);
+}
+
+void FaceGazeTestUtils::SetVelocityThreshold(bool use_threshold) {
+  // TODO(b/309121742): Update this to set the pref value after a pref for
+  // velocity threshold has been added.
+  std::string true_script = "faceGazeTestSupport.setVelocityThreshold(true);";
+  std::string false_script = "faceGazeTestSupport.setVelocityThreshold(false);";
+  ExecuteAccessibilityCommonScript(use_threshold ? true_script : false_script);
+}
+
 void FaceGazeTestUtils::SetGesturesToMacros(
     const base::flat_map<FaceGazeGesture, MacroName>& gestures_to_macros) {
   // Copy the stricly-typed mapping of gestures to macros into a dictionary
diff --git a/chrome/browser/ash/accessibility/facegaze_test_utils.h b/chrome/browser/ash/accessibility/facegaze_test_utils.h
index 523643689..6396569d 100644
--- a/chrome/browser/ash/accessibility/facegaze_test_utils.h
+++ b/chrome/browser/ash/accessibility/facegaze_test_utils.h
@@ -165,11 +165,15 @@
         const base::flat_map<FaceGazeGesture, int>& gesture_confidences);
     Config& WithCursorSpeeds(const CursorSpeeds& speeds);
     Config& WithGestureRepeatDelayMs(int delay);
+    Config& WithLandmarkWeights(bool use_weights);
+    Config& WithVelocityThreshold(bool use_threshold);
 
     const gfx::PointF& forehead_location() const { return forehead_location_; }
     const gfx::Point& cursor_location() const { return cursor_location_; }
     int buffer_size() const { return buffer_size_; }
     bool use_cursor_acceleration() const { return use_cursor_acceleration_; }
+    bool use_landmark_weights() const { return use_landmark_weights_; }
+    bool use_velocity_threshold() const { return use_velocity_threshold_; }
     bool dialog_accepted() const { return dialog_accepted_; }
     const std::optional<base::flat_map<FaceGazeGesture, MacroName>>&
     gestures_to_macros() const {
@@ -192,6 +196,8 @@
     gfx::Point cursor_location_;
     int buffer_size_;
     bool use_cursor_acceleration_;
+    bool use_landmark_weights_;
+    bool use_velocity_threshold_;
     bool dialog_accepted_;
 
     // Optional properties.
@@ -272,6 +278,8 @@
   void SetCursorSpeeds(const CursorSpeeds& speeds);
   void SetBufferSize(int size);
   void SetCursorAcceleration(bool use_acceleration);
+  void SetLandmarkWeights(bool use_weights);
+  void SetVelocityThreshold(bool use_threshold);
   void SetGesturesToMacros(
       const base::flat_map<FaceGazeGesture, MacroName>& gestures_to_macros);
   void SetGestureConfidences(
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index 5772784..aba81d68 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -54,10 +54,10 @@
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/input_method/candidate_window_view.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
diff --git a/chrome/browser/ash/app_list/BUILD.gn b/chrome/browser/ash/app_list/BUILD.gn
index e14beb23..7d6e54d 100644
--- a/chrome/browser/ash/app_list/BUILD.gn
+++ b/chrome/browser/ash/app_list/BUILD.gn
@@ -240,13 +240,13 @@
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/demo_mode:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/system_web_apps",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/search_engines",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/shelf",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/browser/web_applications",
diff --git a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
index 176cb8f..1e09cbd 100644
--- a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
+++ b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
@@ -62,7 +62,6 @@
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/browser_process.h"
@@ -72,6 +71,7 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/ash/app_list/app_list_sort_browsertest.cc b/chrome/browser/ash/app_list/app_list_sort_browsertest.cc
index 96ca65b..8588a0d 100644
--- a/chrome/browser/ash/app_list/app_list_sort_browsertest.cc
+++ b/chrome/browser/ash/app_list/app_list_sort_browsertest.cc
@@ -34,9 +34,9 @@
 #include "chrome/browser/ash/app_list/test/chrome_app_list_test_support.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/icon_loader.h"
diff --git a/chrome/browser/ash/app_mode/BUILD.gn b/chrome/browser/ash/app_mode/BUILD.gn
index 895738e..d22ba6b 100644
--- a/chrome/browser/ash/app_mode/BUILD.gn
+++ b/chrome/browser/ash/app_mode/BUILD.gn
@@ -109,7 +109,6 @@
     "//chrome/browser/ash/login/app_mode",
     "//chrome/browser/ash/login/auth",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/notifications",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/handlers",
@@ -145,7 +144,6 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/app_mode",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/policy/remote_commands/crd",
     "//chrome/browser/chromeos/app_mode",
diff --git a/chrome/browser/ash/app_mode/DEPS b/chrome/browser/ash/app_mode/DEPS
index cc35585f..437f795 100644
--- a/chrome/browser/ash/app_mode/DEPS
+++ b/chrome/browser/ash/app_mode/DEPS
@@ -55,6 +55,7 @@
   "+chrome/browser/policy",
   "+chrome/browser/profiles",
   "+chrome/browser/ui/apps",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/ash/system_web_apps",
   "+chrome/browser/ui/browser.h",
diff --git a/chrome/browser/ash/app_mode/kiosk_controller_impl.cc b/chrome/browser/ash/app_mode/kiosk_controller_impl.cc
index 9d06ecd..d935d558 100644
--- a/chrome/browser/ash/app_mode/kiosk_controller_impl.cc
+++ b/chrome/browser/ash/app_mode/kiosk_controller_impl.cc
@@ -39,9 +39,9 @@
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_data.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
 #include "chrome/browser/ash/login/app_mode/kiosk_launch_controller.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/ash/components/kiosk/vision/internals_page_processor.h"
 #include "chromeos/ash/components/kiosk/vision/kiosk_vision.h"
diff --git a/chrome/browser/ash/app_mode/load_profile.cc b/chrome/browser/ash/app_mode/load_profile.cc
index ec491621..1c3dd287 100644
--- a/chrome/browser/ash/app_mode/load_profile.cc
+++ b/chrome/browser/ash/app_mode/load_profile.cc
@@ -30,7 +30,7 @@
 #include "chrome/browser/ash/app_mode/retry_runner.h"
 #include "chrome/browser/ash/login/auth/chrome_login_performer.h"
 #include "chrome/browser/ash/login/session/user_session_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
 #include "chromeos/ash/components/login/auth/auth_events_recorder.h"
diff --git a/chrome/browser/ash/app_mode/web_app/BUILD.gn b/chrome/browser/ash/app_mode/web_app/BUILD.gn
index ec5c1f1..d9097b7 100644
--- a/chrome/browser/ash/app_mode/web_app/BUILD.gn
+++ b/chrome/browser/ash/app_mode/web_app/BUILD.gn
@@ -36,7 +36,6 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/apps/app_service/app_icon",
     "//chrome/browser/ash/crosapi:browser_util",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/remote_commands/crd",
     "//chrome/browser/chromeos/app_mode",
@@ -49,10 +48,8 @@
     "//services/network/public/cpp",
     "//skia",
   ]
-  allow_circular_includes_from = [
-    "//chrome/browser/ash/login/ui",
-    "//chrome/browser/ash/policy/remote_commands/crd",
-  ]
+  allow_circular_includes_from =
+      [ "//chrome/browser/ash/policy/remote_commands/crd" ]
 }
 
 source_set("unit_tests") {
diff --git a/chrome/browser/ash/app_restore/full_restore_service.cc b/chrome/browser/ash/app_restore/full_restore_service.cc
index abf5681..c130c3c 100644
--- a/chrome/browser/ash/app_restore/full_restore_service.cc
+++ b/chrome/browser/ash/app_restore/full_restore_service.cc
@@ -919,7 +919,14 @@
   auto* contents_data = contents_data_
                             ? contents_data_.get()
                             : delegate_->GetInformedRestoreContentData();
-  CHECK(contents_data);
+
+  // It is possible the user clicks restore or cancel before fetching the
+  // session restore data is complete. In this case, there's no need to update
+  // anything so we can just bail out here. See http://b/365844258 for more
+  // details.
+  if (!contents_data) {
+    return;
+  }
 
   bool content_updated = false;
   for (auto& info : contents_data->apps_infos) {
diff --git a/chrome/browser/ash/arc/BUILD.gn b/chrome/browser/ash/arc/BUILD.gn
index b83270a..7465256c 100644
--- a/chrome/browser/ash/arc/BUILD.gn
+++ b/chrome/browser/ash/arc/BUILD.gn
@@ -157,8 +157,8 @@
     "//chrome/browser/ash/arc/session",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/demo_mode:test_support",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/login/users:test_support",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/test:test_support",
     "//chromeos/ash/components/install_attributes",
diff --git a/chrome/browser/ash/arc/arc_util.cc b/chrome/browser/ash/arc/arc_util.cc
index 7f1c3a73..e755658 100644
--- a/chrome/browser/ash/arc/arc_util.cc
+++ b/chrome/browser/ash/arc/arc_util.cc
@@ -38,7 +38,6 @@
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
 #include "chrome/browser/ash/login/oobe_configuration.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
@@ -46,6 +45,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/simple_message_box.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ash/arc/arc_util_unittest.cc b/chrome/browser/ash/arc/arc_util_unittest.cc
index f92386b..5ee8415 100644
--- a/chrome/browser/ash/arc/arc_util_unittest.cc
+++ b/chrome/browser/ash/arc/arc_util_unittest.cc
@@ -20,13 +20,13 @@
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ash/login/oobe_configuration.h"
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/demo_preferences_screen_handler.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index 4e0002a..7afcf6e 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -940,58 +940,6 @@
   }
 }
 
-void DisplayOverlayController::UpdateButtonOptionsMenuWidgetBounds() {
-  // There is no `button_options_widget_` in view mode.
-  if (!button_options_widget_) {
-    return;
-  }
-
-  if (auto* menu = GetButtonOptionsMenu()) {
-    menu->UpdateWidget();
-  }
-}
-
-void DisplayOverlayController::UpdateInputMappingWidgetBounds() {
-  // There is no `input_mapping_widget_` if there is no active action or gio is
-  // disabled.
-  if (!input_mapping_widget_) {
-    return;
-  }
-
-  UpdateWidgetBoundsInRootWindow(input_mapping_widget_.get(),
-                                 touch_injector_->content_bounds());
-  StackInputMappingAtBottomForViewMode();
-}
-
-void DisplayOverlayController::UpdateEditingListWidgetBounds() {
-  // There is no `editing_list_widget_` in view mode.
-  if (!editing_list_widget_) {
-    return;
-  }
-
-  if (auto* editing_list = GetEditingList()) {
-    editing_list->UpdateWidget();
-  }
-}
-
-void DisplayOverlayController::UpdateTargetWidgetBounds() {
-  if (!target_widget_) {
-    return;
-  }
-
-  if (auto* target_view = GetTargetView()) {
-    target_view->UpdateWidgetBounds();
-  }
-}
-
-ActionViewListItem* DisplayOverlayController::GetEditingListItemForAction(
-    Action* action) {
-  if (auto* editing_list = GetEditingList()) {
-    return editing_list->GetListItemForAction(action);
-  }
-  return nullptr;
-}
-
 void DisplayOverlayController::UpdateWidgetBoundsInRootWindow(
     views::Widget* widget,
     const gfx::Rect& bounds_in_root_window) {
@@ -1002,6 +950,14 @@
   widget->SetBounds(bounds_in_screen);
 }
 
+ActionViewListItem* DisplayOverlayController::GetEditingListItemForAction(
+    Action* action) {
+  if (auto* editing_list = GetEditingList()) {
+    return editing_list->GetListItemForAction(action);
+  }
+  return nullptr;
+}
+
 void DisplayOverlayController::OnMouseEvent(ui::MouseEvent* event) {
   if ((display_mode_ == DisplayMode::kView && IsNudgeEmpty()) ||
       event->type() != ui::EventType::kMousePressed) {
@@ -1473,6 +1429,50 @@
              : nullptr;
 }
 
+void DisplayOverlayController::UpdateButtonOptionsMenuWidgetBounds() {
+  // There is no `button_options_widget_` in view mode.
+  if (!button_options_widget_) {
+    return;
+  }
+
+  if (auto* menu = GetButtonOptionsMenu()) {
+    menu->UpdateWidget();
+  }
+}
+
+void DisplayOverlayController::UpdateInputMappingWidgetBounds() {
+  // There is no `input_mapping_widget_` if there is no active action or gio is
+  // disabled.
+  if (!input_mapping_widget_) {
+    return;
+  }
+
+  UpdateWidgetBoundsInRootWindow(input_mapping_widget_.get(),
+                                 touch_injector_->content_bounds());
+  StackInputMappingAtBottomForViewMode();
+}
+
+void DisplayOverlayController::UpdateEditingListWidgetBounds() {
+  // There is no `editing_list_widget_` in view mode.
+  if (!editing_list_widget_) {
+    return;
+  }
+
+  if (auto* editing_list = GetEditingList()) {
+    editing_list->UpdateWidget();
+  }
+}
+
+void DisplayOverlayController::UpdateTargetWidgetBounds() {
+  if (!target_widget_) {
+    return;
+  }
+
+  if (auto* target_view = GetTargetView()) {
+    target_view->UpdateWidgetBounds();
+  }
+}
+
 void DisplayOverlayController::UpdateEventRewriteCapability() {
   ash::ArcGameControlsFlag flags =
       touch_injector_->window()->GetProperty(ash::kArcGameControlsFlagsKey);
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
index 968efda..e55427f4 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
@@ -134,12 +134,11 @@
   // `action`'s view.
   void HideActionHighlightWidgetForAction(Action* action);
 
-  // Update widget bounds if the view content is changed or the app window
-  // bounds are changed.
-  void UpdateButtonOptionsMenuWidgetBounds();
-  void UpdateInputMappingWidgetBounds();
-  void UpdateEditingListWidgetBounds();
-  void UpdateTargetWidgetBounds();
+  // `widget` bounds is in screen coordinate. `bounds_in_root_window` is the
+  // window bounds in associated game window's root window. Convert
+  // `bounds_in_root_window` in screen coordinates to set `widget` bounds.
+  void UpdateWidgetBoundsInRootWindow(views::Widget* widget,
+                                      const gfx::Rect& bounds_in_root_window);
 
   ActionViewListItem* GetEditingListItemForAction(Action* action);
 
@@ -277,11 +276,12 @@
 
   DeleteEditShortcut* GetDeleteEditShortcut() const;
 
-  // `widget` bounds is in screen coordinate. `bounds_in_root_window` is the
-  // window bounds in root window. Convert `bounds_in_root_window` in screen
-  // coordinates to set `widget` bounds.
-  void UpdateWidgetBoundsInRootWindow(views::Widget* widget,
-                                      const gfx::Rect& bounds_in_root_window);
+  // Update widget bounds if the view content is changed or the app window
+  // bounds are changed.
+  void UpdateButtonOptionsMenuWidgetBounds();
+  void UpdateInputMappingWidgetBounds();
+  void UpdateEditingListWidgetBounds();
+  void UpdateTargetWidgetBounds();
 
   // `TouchInjector` only rewrite events in `kView` mode. When changing between
   // edit mode and view mode or the feature is disabled from menu or if the game
diff --git a/chrome/browser/ash/arc/input_overlay/ui/target_view.cc b/chrome/browser/ash/arc/input_overlay/ui/target_view.cc
index 95e195d..f41c9d4 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/target_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/target_view.cc
@@ -91,7 +91,8 @@
   auto* widget = GetWidget();
   DCHECK(widget);
 
-  widget->SetBounds(controller_->touch_injector()->content_bounds());
+  controller_->UpdateWidgetBoundsInRootWindow(
+      widget, controller_->touch_injector()->content_bounds());
 }
 
 gfx::Rect TargetView::GetTargetCircleBounds() const {
diff --git a/chrome/browser/ash/arc/input_overlay/ui/target_view_unittest.cc b/chrome/browser/ash/arc/input_overlay/ui/target_view_unittest.cc
index 199351a..9af9d906 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/target_view_unittest.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/target_view_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/arc/input_overlay/ui/target_view.h"
 
+#include "ash/shell.h"
 #include "chrome/browser/ash/arc/input_overlay/actions/action.h"
 #include "chrome/browser/ash/arc/input_overlay/constants.h"
 #include "chrome/browser/ash/arc/input_overlay/test/overlay_view_test_base.h"
@@ -201,4 +202,28 @@
                            GetPointInScreenFromTargetView(local_center));
 }
 
+TEST_F(TargetViewTest, TestMultiDisplay) {
+  UpdateDisplay("1000x900,1000x900");
+  aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
+  display::Display display1 = display::Screen::GetScreen()->GetDisplayMatching(
+      root_windows[1]->GetBoundsInScreen());
+
+  // Move the window from the primary display to `display1`.
+  auto* arc_window = widget_->GetNativeWindow();
+  ASSERT_TRUE(arc_window);
+  auto screen_bounds = arc_window->bounds();
+  const auto& display_bounds = display1.bounds();
+  screen_bounds.Offset(display_bounds.x(), display_bounds.y());
+  arc_window->SetBoundsInScreen(screen_bounds, display1);
+  // Update `controller_` since it is updated from switching display.
+  controller_ = GetDisplayOverlayController();
+
+  // Show `target_view` and it should show inside of the window bounds.
+  EnableEditMode();
+  PressAddButton();
+  auto* target_view = GetTargetView();
+  EXPECT_TRUE(target_view);
+  EXPECT_TRUE(screen_bounds.Contains(target_view->GetBoundsInScreen()));
+}
+
 }  // namespace arc::input_overlay
diff --git a/chrome/browser/ash/arc/notification/BUILD.gn b/chrome/browser/ash/arc/notification/BUILD.gn
index 29362510..5079922 100644
--- a/chrome/browser/ash/arc/notification/BUILD.gn
+++ b/chrome/browser/ash/arc/notification/BUILD.gn
@@ -72,8 +72,8 @@
     "//chrome/browser/ash/arc",
     "//chrome/browser/ash/arc/session",
     "//chrome/browser/ash/arc/test:arc_test_support",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/login/users:test_support",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/test:test_support",
     "//components/prefs",
     "//content/test:test_support",
diff --git a/chrome/browser/ash/arc/notification/DEPS b/chrome/browser/ash/arc/notification/DEPS
index 713d7549..27150ee 100644
--- a/chrome/browser/ash/arc/notification/DEPS
+++ b/chrome/browser/ash/arc/notification/DEPS
@@ -18,7 +18,6 @@
   "+chrome/browser/ash/app_list/arc",
   "+chrome/browser/ash/arc",
   "+chrome/browser/ash/login/demo_mode",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/policy/core",
   "+chrome/browser/ash/profiles",
@@ -29,6 +28,7 @@
   "+chrome/browser/policy",
   "+chrome/browser/profiles",
   "+chrome/browser/ui/ash/arc",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/settings_window_manager_chromeos.h",
   "+chrome/common/webui_url_constants.h",
diff --git a/chrome/browser/ash/arc/notification/arc_provision_notification_service_unittest.cc b/chrome/browser/ash/arc/notification/arc_provision_notification_service_unittest.cc
index 3fe78e6..df0d945 100644
--- a/chrome/browser/ash/arc/notification/arc_provision_notification_service_unittest.cc
+++ b/chrome/browser/ash/arc/notification/arc_provision_notification_service_unittest.cc
@@ -22,8 +22,8 @@
 #include "chrome/browser/ash/arc/session/arc_provisioning_result.h"
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/ash/arc/test/test_arc_session_manager.h"
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
diff --git a/chrome/browser/ash/arc/optin/DEPS b/chrome/browser/ash/arc/optin/DEPS
index 17a6aca..ce6045b3 100644
--- a/chrome/browser/ash/arc/optin/DEPS
+++ b/chrome/browser/ash/arc/optin/DEPS
@@ -25,6 +25,7 @@
   "+chrome/browser/metrics",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/webui/ash/login",
   "+chrome/grit",
   "+chrome/test/base",
diff --git a/chrome/browser/ash/arc/optin/arc_terms_of_service_oobe_negotiator.cc b/chrome/browser/ash/arc/optin/arc_terms_of_service_oobe_negotiator.cc
index 3219810..22cf234 100644
--- a/chrome/browser/ash/arc/optin/arc_terms_of_service_oobe_negotiator.cc
+++ b/chrome/browser/ash/arc/optin/arc_terms_of_service_oobe_negotiator.cc
@@ -7,9 +7,9 @@
 #include "ash/constants/ash_features.h"
 #include "base/functional/bind.h"
 #include "chrome/browser/ash/login/screens/consolidated_consent_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 
 namespace arc {
diff --git a/chrome/browser/ash/arc/session/BUILD.gn b/chrome/browser/ash/arc/session/BUILD.gn
index e200a56..0d1628b 100644
--- a/chrome/browser/ash/arc/session/BUILD.gn
+++ b/chrome/browser/ash/arc/session/BUILD.gn
@@ -168,11 +168,11 @@
     "//chrome/browser/ash/arc/test:arc_test_support",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/demo_mode",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/policy/arc:test_support",
     "//chrome/browser/ash/settings",
     "//chrome/browser/ash/settings:test_support",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/test:test_support",
     "//chromeos/ash/components/dbus/arc",
diff --git a/chrome/browser/ash/arc/session/DEPS b/chrome/browser/ash/arc/session/DEPS
index 6a1a223..f1efa810 100644
--- a/chrome/browser/ash/arc/session/DEPS
+++ b/chrome/browser/ash/arc/session/DEPS
@@ -41,6 +41,7 @@
   "+chrome/browser/prefs",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/ash/shelf",
   "+chrome/browser/ui/browser_commands.h",
diff --git a/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler_unittest.cc b/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler_unittest.cc
index 54c7423..dd4882c 100644
--- a/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler_unittest.cc
+++ b/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler_unittest.cc
@@ -20,11 +20,11 @@
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/ash/arc/test/arc_data_removed_waiter.h"
 #include "chrome/browser/ash/arc/test/test_arc_session_manager.h"
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
 #include "chrome/browser/consent_auditor/consent_auditor_test_utils.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
index ae534bb..06809fa 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
@@ -46,7 +46,6 @@
 #include "chrome/browser/ash/arc/test/test_arc_session_manager.h"
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/arc/fake_android_management_client.h"
@@ -57,6 +56,7 @@
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/ash/auth/BUILD.gn b/chrome/browser/ash/auth/BUILD.gn
index c260362..ac6782fb 100644
--- a/chrome/browser/ash/auth/BUILD.gn
+++ b/chrome/browser/ash/auth/BUILD.gn
@@ -24,10 +24,10 @@
     "//base",
     "//chrome/browser:browser_process",
     "//chrome/browser/ash/login/quick_unlock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/settings/pages/privacy",
     "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/dbus/userdataauth",
@@ -39,8 +39,8 @@
   ]
 
   allow_circular_includes_from = [
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/quick_unlock",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/settings/pages/privacy",
   ]
 }
diff --git a/chrome/browser/ash/cert_provisioning/BUILD.gn b/chrome/browser/ash/cert_provisioning/BUILD.gn
index f290a530..79cfea2 100644
--- a/chrome/browser/ash/cert_provisioning/BUILD.gn
+++ b/chrome/browser/ash/cert_provisioning/BUILD.gn
@@ -55,6 +55,9 @@
     "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/dbus/attestation",
     "//chromeos/ash/components/dbus/attestation:attestation_proto",
+    "//chromeos/dbus/common",
+    "//chromeos/dbus/tpm_manager",
+    "//chromeos/dbus/tpm_manager:tpm_manager_proto",
     "//components/invalidation:invalidation",
     "//components/invalidation/impl",
     "//components/policy/core/common",
@@ -102,6 +105,7 @@
     "//chromeos/ash/components/dbus/attestation:attestation_proto",
     "//components/invalidation:test_support",
     "//components/invalidation/impl:test_support",
+    "//crypto:test_support",
     "//net:test_support",
     "//testing/gtest",
   ]
diff --git a/chrome/browser/ash/cert_provisioning/DEPS b/chrome/browser/ash/cert_provisioning/DEPS
index 6902e43..1d024766 100644
--- a/chrome/browser/ash/cert_provisioning/DEPS
+++ b/chrome/browser/ash/cert_provisioning/DEPS
@@ -15,6 +15,8 @@
   # individually. Other dependencies within //chrome are listed on a per-
   # directory basis. See //tools/chromeos/gen_deps.sh for details.
   "+chrome/browser/ash/attestation",
+  "+chrome/browser/ash/crosapi",
+  "+chrome/browser/ash/login/startup_utils.h",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/platform_keys",
   "+chrome/browser/ash/policy/core",
diff --git a/chrome/browser/ash/certs/BUILD.gn b/chrome/browser/ash/certs/BUILD.gn
new file mode 100644
index 0000000..9acdc3de
--- /dev/null
+++ b/chrome/browser/ash/certs/BUILD.gn
@@ -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.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash)
+
+static_library("certs") {
+  sources = [
+    "system_token_cert_db_initializer.cc",
+    "system_token_cert_db_initializer.h",
+  ]
+
+  deps = [
+    "//chrome/browser/ash/login",
+    "//chromeos/ash/components/dbus/userdataauth",
+    "//chromeos/ash/components/tpm",
+    "//chromeos/ash/components/tpm:buildflags",
+    "//chromeos/dbus/tpm_manager",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [ "system_token_cert_db_initializer_unittest.cc" ]
+
+  deps = [
+    ":certs",
+    "//base",
+    "//base/test:test_support",
+    "//chromeos/ash/components/dbus/userdataauth",
+    "//chromeos/ash/components/network",
+    "//chromeos/ash/components/network:test_support",
+    "//chromeos/ash/components/tpm",
+    "//chromeos/dbus/tpm_manager",
+    "//content/test:test_support",
+    "//crypto",
+    "//crypto:test_support",
+    "//net",
+  ]
+}
diff --git a/chrome/browser/ash/certs/DEPS b/chrome/browser/ash/certs/DEPS
new file mode 100644
index 0000000..a2352bee
--- /dev/null
+++ b/chrome/browser/ash/certs/DEPS
@@ -0,0 +1,19 @@
+include_rules = [
+  # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for
+  # details.
+  "-chrome",
+
+  # This directory is in //chrome, which violates the rule above. Allow this
+  # directory to #include its own files.
+  "+chrome/browser/ash/certs",
+
+  # Existing dependencies within //chrome. There is an active effort to
+  # refactor //chrome/browser/ash to break these dependencies; see b/332804822.
+  # Whenever possible, avoid adding new //chrome dependencies to this list.
+  #
+  # Files residing in certain directories (e.g., //chrome/browser) are listed
+  # individually. Other dependencies within //chrome are listed on a per-
+  # directory basis. See //tools/chromeos/gen_deps.sh for details.
+  "+chrome/browser/ash/crosapi",
+  "+chrome/browser/ash/login",
+]
diff --git a/chrome/browser/ash/system_token_cert_db_initializer.cc b/chrome/browser/ash/certs/system_token_cert_db_initializer.cc
similarity index 98%
rename from chrome/browser/ash/system_token_cert_db_initializer.cc
rename to chrome/browser/ash/certs/system_token_cert_db_initializer.cc
index 91a2885..fb14cff 100644
--- a/chrome/browser/ash/system_token_cert_db_initializer.cc
+++ b/chrome/browser/ash/certs/system_token_cert_db_initializer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/system_token_cert_db_initializer.h"
+#include "chrome/browser/ash/certs/system_token_cert_db_initializer.h"
 
 #include <pk11pub.h>
 
@@ -218,8 +218,9 @@
 void SystemTokenCertDBInitializer::MaybeStartInitializingDatabase() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (started_initializing_)
+  if (started_initializing_) {
     return;
+  }
   started_initializing_ = true;
   VLOG(1)
       << "SystemTokenCertDBInitializer: TPM is ready, loading system token.";
diff --git a/chrome/browser/ash/system_token_cert_db_initializer.h b/chrome/browser/ash/certs/system_token_cert_db_initializer.h
similarity index 94%
rename from chrome/browser/ash/system_token_cert_db_initializer.h
rename to chrome/browser/ash/certs/system_token_cert_db_initializer.h
index a80bfd7..985f634 100644
--- a/chrome/browser/ash/system_token_cert_db_initializer.h
+++ b/chrome/browser/ash/certs/system_token_cert_db_initializer.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 CHROME_BROWSER_ASH_SYSTEM_TOKEN_CERT_DB_INITIALIZER_H_
-#define CHROME_BROWSER_ASH_SYSTEM_TOKEN_CERT_DB_INITIALIZER_H_
+#ifndef CHROME_BROWSER_ASH_CERTS_SYSTEM_TOKEN_CERT_DB_INITIALIZER_H_
+#define CHROME_BROWSER_ASH_CERTS_SYSTEM_TOKEN_CERT_DB_INITIALIZER_H_
 
 #include <memory>
 
@@ -107,4 +107,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_SYSTEM_TOKEN_CERT_DB_INITIALIZER_H_
+#endif  // CHROME_BROWSER_ASH_CERTS_SYSTEM_TOKEN_CERT_DB_INITIALIZER_H_
diff --git a/chrome/browser/ash/system_token_cert_db_initializer_unittest.cc b/chrome/browser/ash/certs/system_token_cert_db_initializer_unittest.cc
similarity index 98%
rename from chrome/browser/ash/system_token_cert_db_initializer_unittest.cc
rename to chrome/browser/ash/certs/system_token_cert_db_initializer_unittest.cc
index 6a19a402..c2ed87d 100644
--- a/chrome/browser/ash/system_token_cert_db_initializer_unittest.cc
+++ b/chrome/browser/ash/certs/system_token_cert_db_initializer_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/system_token_cert_db_initializer.h"
+#include "chrome/browser/ash/certs/system_token_cert_db_initializer.h"
 
 #include <memory>
 
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index d3c6d69..b8cb5bca 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -63,6 +63,7 @@
 #include "chrome/browser/ash/bluetooth/hats_bluetooth_revamp_trigger_impl.h"
 #include "chrome/browser/ash/boot_times_recorder/boot_times_recorder.h"
 #include "chrome/browser/ash/camera/camera_general_survey_handler.h"
+#include "chrome/browser/ash/certs/system_token_cert_db_initializer.h"
 #include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
 #include "chrome/browser/ash/crosapi/lacros_availability_policy_observer.h"
@@ -161,7 +162,6 @@
 #include "chrome/browser/ash/smb_client/smb_service_factory.h"
 #include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/ash/system/user_removal_manager.h"
-#include "chrome/browser/ash/system_token_cert_db_initializer.h"
 #include "chrome/browser/ash/usb/cros_usb_detector.h"
 #include "chrome/browser/ash/video_conference/video_conference_app_service_client.h"
 #include "chrome/browser/ash/video_conference/video_conference_ash_feature_client.h"
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index a3ce1f9..994f6db 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -350,6 +350,7 @@
     "//chrome/browser/ash/arc",
     "//chrome/browser/ash/attestation",
     "//chrome/browser/ash/cert_provisioning",
+    "//chrome/browser/ash/certs",
     "//chrome/browser/ash/drive",
     "//chrome/browser/ash/exo",
     "//chrome/browser/ash/extensions/autotest_private",
@@ -367,7 +368,6 @@
     "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/magic_boost",
     "//chrome/browser/ash/mahi",
     "//chrome/browser/ash/mahi/media_app",
@@ -443,6 +443,7 @@
     "//chrome/browser/ui/ash/global_media_controls",
     "//chrome/browser/ui/ash/holding_space",
     "//chrome/browser/ui/ash/keyboard",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/picker",
     "//chrome/browser/ui/ash/session",
     "//chrome/browser/ui/ash/shelf",
@@ -604,6 +605,7 @@
     "//chrome/browser/ash/app_mode/web_app",
     "//chrome/browser/ash/app_restore",
     "//chrome/browser/ash/apps",
+    "//chrome/browser/ash/certs",
     "//chrome/browser/ash/drive",
     "//chrome/browser/ash/extensions/autotest_private",
     "//chrome/browser/ash/file_manager/virtual_tasks",
diff --git a/chrome/browser/ash/crosapi/desk_template_ash.h b/chrome/browser/ash/crosapi/desk_template_ash.h
index f11de8ce..b659f6f 100644
--- a/chrome/browser/ash/crosapi/desk_template_ash.h
+++ b/chrome/browser/ash/crosapi/desk_template_ash.h
@@ -12,6 +12,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "url/gurl.h"
 
 namespace crosapi {
@@ -34,7 +35,7 @@
       base::OnceCallback<void(crosapi::mojom::DeskTemplateStatePtr)> callback);
   void CreateBrowserWithRestoredData(
       const gfx::Rect& bounds,
-      const ui::WindowShowState show_state,
+      const ui::mojom::WindowShowState show_state,
       crosapi::mojom::DeskTemplateStatePtr additional_state);
   void GetFaviconImage(
       const GURL& url,
diff --git a/chrome/browser/ash/crosapi/login_ash.cc b/chrome/browser/ash/crosapi/login_ash.cc
index 71f23eef..7d0ffa74 100644
--- a/chrome/browser/ash/crosapi/login_ash.cc
+++ b/chrome/browser/ash/crosapi/login_ash.cc
@@ -9,13 +9,13 @@
 #include "ash/system/session/guest_session_confirmation_dialog.h"
 #include "base/notreached.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/errors.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/login/auth/public/auth_types.h"
 #include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
diff --git a/chrome/browser/ash/events/BUILD.gn b/chrome/browser/ash/events/BUILD.gn
index be5ef04..c914537 100644
--- a/chrome/browser/ash/events/BUILD.gn
+++ b/chrome/browser/ash/events/BUILD.gn
@@ -23,9 +23,9 @@
     "//ash/public/mojom",
     "//base",
     "//chrome/browser:browser_process",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/notifications",
     "//chrome/browser/extensions",
+    "//chrome/browser/ui/ash/login",
     "//chrome/common",
     "//chromeos/ash/components/install_attributes",
     "//chromeos/ash/components/nearby/common/connections_manager",
diff --git a/chrome/browser/ash/events/DEPS b/chrome/browser/ash/events/DEPS
index b98f3d9..bceb6e6 100644
--- a/chrome/browser/ash/events/DEPS
+++ b/chrome/browser/ash/events/DEPS
@@ -15,13 +15,13 @@
   # individually. Other dependencies within //chrome are listed on a per-
   # directory basis. See //tools/chromeos/gen_deps.sh for details.
   "+chrome/browser/ash/input_method",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/notifications",
   "+chrome/browser/ash/preferences/preferences.h",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/extensions/extension_commands_global_registry.h",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/common/pref_names.h",
   "+chrome/test/base",
 ]
diff --git a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
index 6aec6e5..e6e794b 100644
--- a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
+++ b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
@@ -14,10 +14,10 @@
 #include "ash/system/input_device_settings/input_device_settings_notification_controller.h"
 #include "base/containers/fixed_flat_map.h"
 #include "base/notreached.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/notifications/deprecation_notification_controller.h"
 #include "chrome/browser/extensions/extension_commands_global_registry.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/extensions/file_manager/BUILD.gn b/chrome/browser/ash/extensions/file_manager/BUILD.gn
index 36f496a..da8e591 100644
--- a/chrome/browser/ash/extensions/file_manager/BUILD.gn
+++ b/chrome/browser/ash/extensions/file_manager/BUILD.gn
@@ -92,7 +92,6 @@
     "//chrome/browser/ash/fusebox",
     "//chrome/browser/ash/guest_os",
     "//chrome/browser/ash/guest_os/public",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/plugin_vm",
     "//chrome/browser/ash/policy/dlp",
     "//chrome/browser/ash/policy/dlp/dialogs",
@@ -110,6 +109,7 @@
     "//chrome/browser/pdf:pdf_pref_names",
     "//chrome/browser/pdf:pdf_service",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/webui/ash/manage_mirrorsync",
     "//chrome/common",
diff --git a/chrome/browser/ash/extensions/file_manager/DEPS b/chrome/browser/ash/extensions/file_manager/DEPS
index 44bd5bc..a719a318 100644
--- a/chrome/browser/ash/extensions/file_manager/DEPS
+++ b/chrome/browser/ash/extensions/file_manager/DEPS
@@ -28,7 +28,6 @@
   "+chrome/browser/ash/fusebox",
   "+chrome/browser/ash/guest_os",
   "+chrome/browser/ash/login/lock",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/plugin_vm",
   "+chrome/browser/ash/policy/dlp",
   "+chrome/browser/ash/policy/skyvault",
@@ -57,6 +56,7 @@
   "+chrome/browser/sharesheet",
   "+chrome/browser/signin",
   "+chrome/browser/ui/ash/holding_space",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/ash/system_web_apps",
   "+chrome/browser/ui/browser.h",
diff --git a/chrome/browser/ash/extensions/file_manager/event_router.cc b/chrome/browser/ash/extensions/file_manager/event_router.cc
index 72bcb7b..71141a1 100644
--- a/chrome/browser/ash/extensions/file_manager/event_router.cc
+++ b/chrome/browser/ash/extensions/file_manager/event_router.cc
@@ -50,7 +50,6 @@
 #include "chrome/browser/ash/guest_os/guest_os_share_path.h"
 #include "chrome/browser/ash/guest_os/public/guest_os_service.h"
 #include "chrome/browser/ash/login/lock/screen_locker.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/ash/policy/dlp/dialogs/files_policy_dialog.h"
 #include "chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.h"
@@ -58,6 +57,7 @@
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/file_manager_private.h"
diff --git a/chrome/browser/ash/extensions/input_method_api.cc b/chrome/browser/ash/extensions/input_method_api.cc
index e2d907ea..57443c7 100644
--- a/chrome/browser/ash/extensions/input_method_api.cc
+++ b/chrome/browser/ash/extensions/input_method_api.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ash/extensions/language_packs/language_packs_extensions_util.h"
 #include "chrome/browser/ash/input_method/autocorrect_manager.h"
 #include "chrome/browser/ash/input_method/native_input_method_engine.h"
-#include "chrome/browser/ash/url_handler/os_url_handler.h"
 #include "chrome/browser/extensions/api/input_ime/input_ime_api.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/spellchecker/spellcheck_factory.h"
@@ -296,16 +295,6 @@
 
   const GURL& options_page_url = ime->options_page_url();
   if (!options_page_url.is_empty()) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    // If Lacros is the only browser, open the options page in an Ash app window
-    // instead of a regular Ash browser window.
-    if (!crosapi::browser_util::IsAshWebBrowserEnabled() &&
-        !chromeos::IsKioskSession()) {
-      bool launched = ash::TryLaunchOsUrlHandler(options_page_url);
-      DCHECK(launched);
-      return RespondNow(NoArguments());
-    }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     content::WebContents* web_contents = GetSenderWebContents();
     if (web_contents) {
       Browser* browser = chrome::FindBrowserWithTab(web_contents);
diff --git a/chrome/browser/ash/extensions/login_screen_ui/BUILD.gn b/chrome/browser/ash/extensions/login_screen_ui/BUILD.gn
index 02601028..13e7745 100644
--- a/chrome/browser/ash/extensions/login_screen_ui/BUILD.gn
+++ b/chrome/browser/ash/extensions/login_screen_ui/BUILD.gn
@@ -17,9 +17,9 @@
   deps = [
     "//ash/public/cpp",
     "//base",
-    "//chrome/browser/ash/login/ui/login_screen_extension_ui",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login/login_screen_extension_ui",
     "//chrome/common",
     "//chromeos/ash/components/install_attributes",
     "//components/session_manager/core",
@@ -38,11 +38,11 @@
     ":login_screen_ui",
     "//base",
     "//base/test:test_support",
-    "//chrome/browser/ash/login/ui/login_screen_extension_ui",
     "//chrome/browser/ash/settings:test_support",
     "//chrome/browser/ui/ash",
     "//chrome/browser/ui/ash:test_support",
     "//chrome/browser/ui/ash/login:test_support",
+    "//chrome/browser/ui/ash/login/login_screen_extension_ui",
     "//chrome/common",
     "//chrome/test:test_support",
     "//chromeos/ash/components/install_attributes:test_support",
@@ -67,10 +67,10 @@
     "//ash/constants",
     "//ash/public/cpp",
     "//base",
-    "//chrome/browser/ash/login/ui/login_screen_extension_ui",
     "//chrome/browser/ash/policy/login:test_support",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login/login_screen_extension_ui",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//components/version_info",
diff --git a/chrome/browser/ash/extensions/login_screen_ui/DEPS b/chrome/browser/ash/extensions/login_screen_ui/DEPS
index e316b14f..7efb18e 100644
--- a/chrome/browser/ash/extensions/login_screen_ui/DEPS
+++ b/chrome/browser/ash/extensions/login_screen_ui/DEPS
@@ -14,13 +14,12 @@
   # Files residing in certain directories (e.g., //chrome/browser) are listed
   # individually. Other dependencies within //chrome are listed on a per-
   # directory basis. See //tools/chromeos/gen_deps.sh for details.
-  "+chrome/browser/ash/login/ui/login_screen_extension_ui",
   "+chrome/browser/ash/policy/login",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/ash/settings",
   "+chrome/browser/chromeos/extensions/login_screen",
   "+chrome/browser/profiles",
-  "+chrome/browser/ui/ash",
+  "+chrome/browser/ui/ash/login",
   "+chrome/common/chrome_constants.h",
   "+chrome/common/extensions/api",
   "+chrome/test/base",
diff --git a/chrome/browser/ash/extensions/login_screen_ui/login_screen_ui_apitest.cc b/chrome/browser/ash/extensions/login_screen_ui/login_screen_ui_apitest.cc
index 92a6853..ae00540e4 100644
--- a/chrome/browser/ash/extensions/login_screen_ui/login_screen_ui_apitest.cc
+++ b/chrome/browser/ash/extensions/login_screen_ui/login_screen_ui_apitest.cc
@@ -6,10 +6,10 @@
 #include <string>
 
 #include "chrome/browser/ash/extensions/login_screen_ui/ui_handler.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/window.h"
 #include "chrome/browser/ash/policy/login/signin_profile_extensions_policy_test_base.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login_screen_apitest_base.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/window.h"
 #include "components/version_info/version_info.h"
 #include "content/public/test/browser_test.h"
 #include "ui/views/widget/widget.h"
diff --git a/chrome/browser/ash/extensions/login_screen_ui/ui_handler.cc b/chrome/browser/ash/extensions/login_screen_ui/ui_handler.cc
index 879290e..d92723d 100644
--- a/chrome/browser/ash/extensions/login_screen_ui/ui_handler.cc
+++ b/chrome/browser/ash/extensions/login_screen_ui/ui_handler.cc
@@ -11,10 +11,10 @@
 #include "ash/public/cpp/login_screen_model.h"
 #include "ash/public/cpp/login_types.h"
 #include "base/trace_event/trace_event.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/window.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/window.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/ash/extensions/login_screen_ui/ui_handler_unittest.cc b/chrome/browser/ash/extensions/login_screen_ui/ui_handler_unittest.cc
index aafde84f..ae69934 100644
--- a/chrome/browser/ash/extensions/login_screen_ui/ui_handler_unittest.cc
+++ b/chrome/browser/ash/extensions/login_screen_ui/ui_handler_unittest.cc
@@ -9,9 +9,9 @@
 #include "base/memory/raw_ptr.h"
 #include "base/test/gtest_util.h"
 #include "base/test/mock_callback.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/window.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/window.h"
 #include "chrome/browser/ui/ash/login/test_login_screen.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/ash/first_run/BUILD.gn b/chrome/browser/ash/first_run/BUILD.gn
index 3249d3c..ded2adb 100644
--- a/chrome/browser/ash/first_run/BUILD.gn
+++ b/chrome/browser/ash/first_run/BUILD.gn
@@ -22,10 +22,10 @@
     "//chrome/browser/ash/arc",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/system_web_apps",
     "//chrome/browser/profiles",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/common",
     "//chromeos/ash/components/login/login_state",
@@ -47,6 +47,6 @@
 
   allow_circular_includes_from = [
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
+    "//chrome/browser/ui/ash/login",
   ]
 }
diff --git a/chrome/browser/ash/first_run/DEPS b/chrome/browser/ash/first_run/DEPS
index 561462f8..b65e987 100644
--- a/chrome/browser/ash/first_run/DEPS
+++ b/chrome/browser/ash/first_run/DEPS
@@ -21,6 +21,7 @@
   "+chrome/browser/policy",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/system_web_apps",
   "+chrome/common/chrome_switches.h",
   "+chrome/common/extensions",
diff --git a/chrome/browser/ash/first_run/first_run.cc b/chrome/browser/ash/first_run/first_run.cc
index 9fcfcdd..c2c4376a 100644
--- a/chrome/browser/ash/first_run/first_run.cc
+++ b/chrome/browser/ash/first_run/first_run.cc
@@ -12,7 +12,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/ash/arc/arc_util.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/browser_process.h"
@@ -20,6 +19,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_constants.h"
diff --git a/chrome/browser/ash/login/BUILD.gn b/chrome/browser/ash/login/BUILD.gn
index 83e2b16..af3b3717 100644
--- a/chrome/browser/ash/login/BUILD.gn
+++ b/chrome/browser/ash/login/BUILD.gn
@@ -156,7 +156,6 @@
     "//chrome/browser/ash/login/oobe_quick_start",
     "//chrome/browser/ash/login/oobe_quick_start:oobe_quick_start_pref_names",
     "//chrome/browser/ash/login/quick_unlock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users/avatar",
     "//chrome/browser/ash/net",
     "//chrome/browser/ash/net/rollback_network_config",
@@ -263,7 +262,6 @@
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/saml",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users/avatar",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/enrollment",
@@ -300,7 +298,7 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/session:test_support",
-    "//chrome/browser/ash/login/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/test:test_support",
     "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/dbus/userdataauth",
@@ -426,8 +424,6 @@
     "//chrome/browser/ash/login/session:test_support",
     "//chrome/browser/ash/login/signin",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/login/users",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/net",
@@ -445,6 +441,7 @@
     "//chrome/browser/ui",
     "//chrome/browser/ui/ash/keyboard",
     "//chrome/browser/ui/ash/login",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/browser/ui/ash/shell_delegate",
     "//chrome/browser/ui/webui/ash/system_web_dialog",
     "//chrome/browser/ui/webui/signin",
@@ -518,7 +515,6 @@
     "screens:browser_tests",
     "session:browser_tests",
     "signin:browser_tests",
-    "ui:browser_tests",
     "users:browser_tests",
   ]
 }
@@ -555,7 +551,6 @@
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/enrollment:test_support",
     "//chrome/browser/ash/login/screens",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/net",
     "//chrome/browser/ash/net/rollback_network_config",
@@ -570,6 +565,7 @@
     "//chrome/browser/ui",
     "//chrome/browser/ui:test_support",
     "//chrome/browser/ui/ash/keyboard:ash_test_support",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/browser/ui/ash/wallpaper",
     "//chrome/browser/ui/ash/wallpaper:test_support",
     "//chrome/common:non_code_constants",
@@ -630,7 +626,6 @@
     "session:unit_tests",
     "signin:unit_tests",
     "smart_lock:unit_tests",
-    "ui:unit_tests",
     "users:unit_tests",
     "version_updater:unit_tests",
   ]
diff --git a/chrome/browser/ash/login/accessibility_browsertest.cc b/chrome/browser/ash/login/accessibility_browsertest.cc
index bfbc231..1ba2ba0 100644
--- a/chrome/browser/ash/login/accessibility_browsertest.cc
+++ b/chrome/browser/ash/login/accessibility_browsertest.cc
@@ -8,9 +8,9 @@
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/test/browser_test.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ash/login/account_supervision_change_browsertest.cc b/chrome/browser/ash/login/account_supervision_change_browsertest.cc
index 89deaef9..8328bd5 100644
--- a/chrome/browser/ash/login/account_supervision_change_browsertest.cc
+++ b/chrome/browser/ash/login/account_supervision_change_browsertest.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
diff --git a/chrome/browser/ash/login/app_mode/BUILD.gn b/chrome/browser/ash/login/app_mode/BUILD.gn
index 29f4b620..40cb582 100644
--- a/chrome/browser/ash/login/app_mode/BUILD.gn
+++ b/chrome/browser/ash/login/app_mode/BUILD.gn
@@ -29,7 +29,6 @@
     "//chrome/browser/ash/app_mode/web_app",
     "//chrome/browser/ash/crosapi:browser_util",
     "//chrome/browser/ash/login",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/ash/keyboard",
diff --git a/chrome/browser/ash/login/app_mode/DEPS b/chrome/browser/ash/login/app_mode/DEPS
index df1470a9..9e0d5e9 100644
--- a/chrome/browser/ash/login/app_mode/DEPS
+++ b/chrome/browser/ash/login/app_mode/DEPS
@@ -41,6 +41,7 @@
   "+chrome/browser/signin",
   "+chrome/browser/speech/extension_api",
   "+chrome/browser/ui/ash/keyboard",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/browser.h",
   "+chrome/browser/ui/browser_list.h",
   "+chrome/browser/ui/browser_navigator.h",
diff --git a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
index 2bd4a052..a059e4e 100644
--- a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
+++ b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
@@ -50,13 +50,13 @@
 #include "chrome/browser/ash/login/app_mode/kiosk_launch_controller.h"
 #include "chrome/browser/ash/login/app_mode/network_ui_controller.h"
 #include "chrome/browser/ash/login/enterprise_user_session_metrics.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
diff --git a/chrome/browser/ash/login/app_mode/network_ui_controller.cc b/chrome/browser/ash/login/app_mode/network_ui_controller.cc
index 6692742..5db1f9a 100644
--- a/chrome/browser/ash/login/app_mode/network_ui_controller.cc
+++ b/chrome/browser/ash/login/app_mode/network_ui_controller.cc
@@ -17,7 +17,7 @@
 #include "base/syslog_logging.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/login/screens/network_error.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/network_state_informer.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
diff --git a/chrome/browser/ash/login/app_mode/test/BUILD.gn b/chrome/browser/ash/login/app_mode/test/BUILD.gn
index ae35c68..90002bc 100644
--- a/chrome/browser/ash/login/app_mode/test/BUILD.gn
+++ b/chrome/browser/ash/login/app_mode/test/BUILD.gn
@@ -64,11 +64,11 @@
     "//chrome/browser/ash/crosapi",
     "//chrome/browser/ash/crosapi:browser_util",
     "//chrome/browser/ash/login",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/settings:test_support",
     "//chrome/browser/chromeos/app_mode",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chromeos/ash/components/standalone_browser",
     "//components/policy:generated",
     "//components/policy:policy_code_generate",
@@ -130,7 +130,6 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/ownership",
     "//chrome/browser/ash/policy/core",
@@ -143,6 +142,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
     "//chrome/browser/ui:browser_navigator_params_headers",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/web_applications",
     "//chrome/common:chrome_features",
diff --git a/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc b/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
index a4c65cc..d0ed4c1 100644
--- a/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
+++ b/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
@@ -33,11 +33,11 @@
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/profiles/profile_impl.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/app_window/app_window.h"
diff --git a/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc b/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
index 222a457..72539011 100644
--- a/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
@@ -29,13 +29,13 @@
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h"
 #include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_navigator.h"
diff --git a/chrome/browser/ash/login/app_mode/test/kiosk_enterprise_browsertest.cc b/chrome/browser/ash/login/app_mode/test/kiosk_enterprise_browsertest.cc
index d1b067a..691c826 100644
--- a/chrome/browser/ash/login/app_mode/test/kiosk_enterprise_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/test/kiosk_enterprise_browsertest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/ash/login/test/embedded_test_server_setup_mixin.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
 #include "chrome/browser/device_identity/device_oauth2_token_service.h"
 #include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
@@ -36,6 +35,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/ash/login/app_mode/test/web_kiosk_browsertest.cc b/chrome/browser/ash/login/app_mode/test/web_kiosk_browsertest.cc
index a596c105..f7c2e05 100644
--- a/chrome/browser/ash/login/app_mode/test/web_kiosk_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/test/web_kiosk_browsertest.cc
@@ -32,10 +32,10 @@
 #include "chrome/browser/ash/login/app_mode/test/web_kiosk_base_test.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/ownership/fake_owner_settings_service.h"
 #include "chrome/browser/chromeos/app_mode/web_kiosk_app_installer.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/test/test_browser_closed_waiter.h"
diff --git a/chrome/browser/ash/login/choobe_flow_controller.cc b/chrome/browser/ash/login/choobe_flow_controller.cc
index 3a88467..efb7250 100644
--- a/chrome/browser/ash/login/choobe_flow_controller.cc
+++ b/chrome/browser/ash/login/choobe_flow_controller.cc
@@ -12,10 +12,10 @@
 #include "base/strings/string_util.h"
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/display_size_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/drive_pinning_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/theme_selection_screen_handler.h"
diff --git a/chrome/browser/ash/login/chrome_restart_request.cc b/chrome/browser/ash/login/chrome_restart_request.cc
index 1b2194bd..b648f53 100644
--- a/chrome/browser/ash/login/chrome_restart_request.cc
+++ b/chrome/browser/ash/login/chrome_restart_request.cc
@@ -247,12 +247,13 @@
 // current session.
 void DeriveFeatures(base::CommandLine* out_command_line) {
   auto kForwardFeatures = {
-    &features::kAutoNightLight,
-    &ash::features::kSeamlessRefreshRateSwitching,
-    &ash::standalone_browser::features::kLacrosOnly,
-    &::features::kPluginVm,
+      &features::kAutoNightLight,
+      &ash::features::kSeamlessRefreshRateSwitching,
+      &ash::standalone_browser::features::kLacrosOnly,
+      &::features::kPluginVm,
+      &display::features::kOledScaleFactorEnabled,
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
-    &media::kPlatformHEVCDecoderSupport,
+      &media::kPlatformHEVCDecoderSupport,
 #endif
   };
   std::vector<std::string> enabled_features;
diff --git a/chrome/browser/ash/login/demo_mode/BUILD.gn b/chrome/browser/ash/login/demo_mode/BUILD.gn
index d7a9cbd..ae231eb 100644
--- a/chrome/browser/ash/login/demo_mode/BUILD.gn
+++ b/chrome/browser/ash/login/demo_mode/BUILD.gn
@@ -143,11 +143,11 @@
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/enrollment",
     "//chrome/browser/ash/system_web_apps",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/test:test_support",
diff --git a/chrome/browser/ash/login/demo_mode/demo_setup_browsertest.cc b/chrome/browser/ash/login/demo_mode/demo_setup_browsertest.cc
index 7fbccf5..2a90797 100644
--- a/chrome/browser/ash/login/demo_mode/demo_setup_browsertest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_setup_browsertest.cc
@@ -48,13 +48,13 @@
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/test_condition_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_status.h"
 #include "chrome/browser/chrome_browser_main.h"
 #include "chrome/browser/chrome_browser_main_extra_parts.h"
 #include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/demo_preferences_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/demo_setup_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
diff --git a/chrome/browser/ash/login/enable_debugging_browsertest.cc b/chrome/browser/ash/login/enable_debugging_browsertest.cc
index 1e546b3..a165b81 100644
--- a/chrome/browser/ash/login/enable_debugging_browsertest.cc
+++ b/chrome/browser/ash/login/enable_debugging_browsertest.cc
@@ -16,9 +16,9 @@
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/login/enable_debugging_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/common/chrome_constants.h"
diff --git a/chrome/browser/ash/login/encryption_migration_browsertest.cc b/chrome/browser/ash/login/encryption_migration_browsertest.cc
index fa42b6c..795ca054 100644
--- a/chrome/browser/ash/login/encryption_migration_browsertest.cc
+++ b/chrome/browser/ash/login/encryption_migration_browsertest.cc
@@ -20,9 +20,9 @@
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
diff --git a/chrome/browser/ash/login/enrollment/BUILD.gn b/chrome/browser/ash/login/enrollment/BUILD.gn
index 144d0be6..08de2b1 100644
--- a/chrome/browser/ash/login/enrollment/BUILD.gn
+++ b/chrome/browser/ash/login/enrollment/BUILD.gn
@@ -38,10 +38,10 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/ash/attestation",
     "//chrome/browser/ash/login/demo_mode",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/enrollment",
     "//chrome/browser/ash/policy/handlers",
+    "//chrome/browser/ui/ash/login",
     "//chromeos/ash/components/attestation",
     "//chromeos/ash/components/dbus",
     "//chromeos/ash/components/dbus/attestation",
@@ -107,13 +107,13 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/ash/login/app_mode/test:browser_tests",
     "//chrome/browser/ash/login/app_mode/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/enrollment",
     "//chrome/browser/ash/policy/enrollment/psm:test_support",
     "//chrome/browser/ash/policy/server_backed_state",
     "//chrome/browser/ash/policy/test_support",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common:constants",
     "//chrome/test:test_support_ui",
@@ -152,9 +152,9 @@
     "//base/test:test_support",
     "//chrome/browser",
     "//chrome/browser/ash/login/screens:test_support",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/policy/enrollment",
     "//chrome/browser/ash/policy/enrollment:test_support",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/common:constants",
     "//chrome/test:test_support",
     "//chromeos/ash/components/install_attributes:test_support",
diff --git a/chrome/browser/ash/login/enrollment/DEPS b/chrome/browser/ash/login/enrollment/DEPS
index 5c17778..e43bed38 100644
--- a/chrome/browser/ash/login/enrollment/DEPS
+++ b/chrome/browser/ash/login/enrollment/DEPS
@@ -29,6 +29,7 @@
   "+chrome/browser/lifetime",
   "+chrome/browser/net",
   "+chrome/browser/prefs",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/webui/ash/login",
   "+chrome/common/chrome_paths.h",
   "+chrome/common/pref_names.h",
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 a0d439d..5079f3d 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
@@ -35,7 +35,6 @@
 #include "chrome/browser/ash/login/test/scoped_policy_update.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/test_condition_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/ownership/fake_owner_settings_service.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -48,6 +47,7 @@
 #include "chrome/browser/ash/policy/test_support/policy_test_server_constants.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/device_disabled_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen.cc b/chrome/browser/ash/login/enrollment/enrollment_screen.cc
index 8f4591451..ddd15f6 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/ash/login/screens/base_screen.h"
 #include "chrome/browser/ash/login/screens/error_screen.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -40,6 +39,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/online_login_utils.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
index 63f62a79..03d588f9 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
@@ -22,12 +22,12 @@
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_config.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_status.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/online_login_utils.h"
 #include "chrome/browser/ui/webui/ash/login/tpm_error_screen_handler.h"
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc b/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
index 7f61532..94feb2471 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen_unittest.cc
@@ -23,13 +23,13 @@
 #include "chrome/browser/ash/login/enrollment/mock_enrollment_launcher.h"
 #include "chrome/browser/ash/login/enrollment/mock_enrollment_screen.h"
 #include "chrome/browser/ash/login/screens/mock_error_screen.h"
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_config.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_status.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_test_helper.h"
 #include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/online_login_utils.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/ash/login/enterprise_enrollment_browsertest.cc b/chrome/browser/ash/login/enterprise_enrollment_browsertest.cc
index 32e5e023..5b226d0 100644
--- a/chrome/browser/ash/login/enterprise_enrollment_browsertest.cc
+++ b/chrome/browser/ash/login/enterprise_enrollment_browsertest.cc
@@ -18,10 +18,10 @@
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_status.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/online_login_utils.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/dbus/upstart/upstart_client.h"
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index 9c651f8..9dad982 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -54,11 +54,6 @@
 #include "chrome/browser/ash/login/signin/oauth2_token_initializer.h"
 #include "chrome/browser/ash/login/signin_specifics.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager_util.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -76,6 +71,11 @@
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/chrome_device_id_helper.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_mojo.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/ash/system/system_tray_client_impl.h"
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
 #include "chrome/browser/ui/managed_ui.h"
diff --git a/chrome/browser/ash/login/existing_user_controller_auto_login_unittest.cc b/chrome/browser/ash/login/existing_user_controller_auto_login_unittest.cc
index ccdcc5be..3f8b383 100644
--- a/chrome/browser/ash/login/existing_user_controller_auto_login_unittest.cc
+++ b/chrome/browser/ash/login/existing_user_controller_auto_login_unittest.cc
@@ -8,10 +8,10 @@
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
-#include "chrome/browser/ash/login/ui/mock_login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/ui/ash/login/mock_login_display_host.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
diff --git a/chrome/browser/ash/login/existing_user_controller_browsertest.cc b/chrome/browser/ash/login/existing_user_controller_browsertest.cc
index 6003e21..73c7d52 100644
--- a/chrome/browser/ash/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/ash/login/existing_user_controller_browsertest.cc
@@ -41,9 +41,6 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/mock_login_display_host.h"
-#include "chrome/browser/ash/login/ui/mock_signin_ui.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -53,6 +50,9 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/mock_login_display_host.h"
+#include "chrome/browser/ui/ash/login/mock_signin_ui.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.h"
diff --git a/chrome/browser/ash/login/help_app_launcher.cc b/chrome/browser/ash/login/help_app_launcher.cc
index 27e447a..4e8ae2d6 100644
--- a/chrome/browser/ash/login/help_app_launcher.cc
+++ b/chrome/browser/ash/login/help_app_launcher.cc
@@ -8,9 +8,9 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ash/login/ui/login_web_dialog.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_web_dialog.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/locale_settings.h"
diff --git a/chrome/browser/ash/login/helper.cc b/chrome/browser/ash/login/helper.cc
index 81dbe1e4..f39c3d4 100644
--- a/chrome/browser/ash/login/helper.cc
+++ b/chrome/browser/ash/login/helper.cc
@@ -13,14 +13,14 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/default_clock.h"
 #include "chrome/browser/ash/login/signin_partition_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/policy/core/device_local_account_policy_broker.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/password_manager/password_reuse_manager_factory.h"
 #include "chrome/browser/policy/networking/user_network_configuration_updater_ash.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
diff --git a/chrome/browser/ash/login/lock/BUILD.gn b/chrome/browser/ash/login/lock/BUILD.gn
index f60999a4f..367ba89 100644
--- a/chrome/browser/ash/login/lock/BUILD.gn
+++ b/chrome/browser/ash/login/lock/BUILD.gn
@@ -46,11 +46,11 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/system",
     "//chrome/browser/extensions",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chrome/common:constants",
     "//chrome/common:non_code_constants",
     "//chromeos/ash/components/browser_context_helper",
@@ -78,6 +78,7 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/extensions",
+    "//chrome/browser/ui/ash/login",
   ]
 }
 
@@ -150,9 +151,9 @@
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/test:test_support_ui",
     "//chromeos/ash/components/dbus/biod",
     "//chromeos/ash/components/dbus/session_manager",
diff --git a/chrome/browser/ash/login/lock/lock_screen_browsertest.cc b/chrome/browser/ash/login/lock/lock_screen_browsertest.cc
index f10cd0c4..c94c0f6 100644
--- a/chrome/browser/ash/login/lock/lock_screen_browsertest.cc
+++ b/chrome/browser/ash/login/lock/lock_screen_browsertest.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/login/lock/screen_locker.cc b/chrome/browser/ash/login/lock/screen_locker.cc
index d2066bc..fda21ef 100644
--- a/chrome/browser/ash/login/lock/screen_locker.cc
+++ b/chrome/browser/ash/login/lock/screen_locker.cc
@@ -37,7 +37,6 @@
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h"
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/ash/login/session/user_session_manager.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/certificate_provider/certificate_provider_service.h"
 #include "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
@@ -45,6 +44,7 @@
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/session/session_controller_client_impl.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/browser_resources.h"
diff --git a/chrome/browser/ash/login/lock/screen_locker_browsertest.cc b/chrome/browser/ash/login/lock/screen_locker_browsertest.cc
index 88947d0d..76f0080f 100644
--- a/chrome/browser/ash/login/lock/screen_locker_browsertest.cc
+++ b/chrome/browser/ash/login/lock/screen_locker_browsertest.cc
@@ -13,8 +13,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/ash/login/lock/screen_locker_tester.h"
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
diff --git a/chrome/browser/ash/login/login_after_update_to_flex_browsertest.cc b/chrome/browser/ash/login/login_after_update_to_flex_browsertest.cc
index fcf1184..8876098 100644
--- a/chrome/browser/ash/login/login_after_update_to_flex_browsertest.cc
+++ b/chrome/browser/ash/login/login_after_update_to_flex_browsertest.cc
@@ -17,12 +17,12 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/hardware_data_collection_screen_handler.h"
 #include "components/user_manager/scoped_user_manager.h"
diff --git a/chrome/browser/ash/login/login_browsertest.cc b/chrome/browser/ash/login/login_browsertest.cc
index 9c4cd0bb0..24537d2 100644
--- a/chrome/browser/ash/login/login_browsertest.cc
+++ b/chrome/browser/ash/login/login_browsertest.cc
@@ -34,9 +34,9 @@
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
 #include "chrome/browser/ash/login/test/user_adding_screen_utils.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
diff --git a/chrome/browser/ash/login/login_manager_test.cc b/chrome/browser/ash/login/login_manager_test.cc
index ff2293f6..f3bcfd6 100644
--- a/chrome/browser/ash/login/login_manager_test.cc
+++ b/chrome/browser/ash/login/login_manager_test.cc
@@ -17,8 +17,8 @@
 #include "chrome/browser/ash/login/session/user_session_manager.h"
 #include "chrome/browser/ash/login/session/user_session_manager_test_api.h"
 #include "chrome/browser/ash/login/test/profile_prepared_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chromeos/ash/components/cryptohome/system_salt_getter.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
diff --git a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
index 0a09a5a..87565d4 100644
--- a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
+++ b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
@@ -24,14 +24,14 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/scoped_policy_update.h"
 #include "chrome/browser/ash/login/test/user_adding_screen_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/language_preferences/language_preferences.h"
diff --git a/chrome/browser/ash/login/login_ui_shelf_visibility_browsertest.cc b/chrome/browser/ash/login/login_ui_shelf_visibility_browsertest.cc
index dc0b5745..22bf277 100644
--- a/chrome/browser/ash/login/login_ui_shelf_visibility_browsertest.cc
+++ b/chrome/browser/ash/login/login_ui_shelf_visibility_browsertest.cc
@@ -15,10 +15,10 @@
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/scoped_policy_update.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/ash/login/os_install_screen_handler.h"
diff --git a/chrome/browser/ash/login/misconfigured_user_browsertest.cc b/chrome/browser/ash/login/misconfigured_user_browsertest.cc
index 9e74ec24..1b4fa06 100644
--- a/chrome/browser/ash/login/misconfigured_user_browsertest.cc
+++ b/chrome/browser/ash/login/misconfigured_user_browsertest.cc
@@ -18,10 +18,10 @@
 #include "chrome/browser/ash/login/test/cryptohome_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
diff --git a/chrome/browser/ash/login/oobe_browsertest.cc b/chrome/browser/ash/login/oobe_browsertest.cc
index 0d894621..8698f9f 100644
--- a/chrome/browser/ash/login/oobe_browsertest.cc
+++ b/chrome/browser/ash/login/oobe_browsertest.cc
@@ -18,12 +18,12 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/update_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
diff --git a/chrome/browser/ash/login/oobe_interactive_ui_test.cc b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
index 927bd54..5116bf1d 100644
--- a/chrome/browser/ash/login/oobe_interactive_ui_test.cc
+++ b/chrome/browser/ash/login/oobe_interactive_ui_test.cc
@@ -46,7 +46,6 @@
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/scoped_policy_update.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/auto_enrollment_controller.h"
 #include "chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h"
@@ -57,6 +56,7 @@
 #include "chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/ai_intro_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/app_downloading_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/assistant_optin_flow_screen_handler.h"
diff --git a/chrome/browser/ash/login/oobe_localization_browsertest.cc b/chrome/browser/ash/login/oobe_localization_browsertest.cc
index 3c81ce15..edb4514 100644
--- a/chrome/browser/ash/login/oobe_localization_browsertest.cc
+++ b/chrome/browser/ash/login/oobe_localization_browsertest.cc
@@ -21,9 +21,9 @@
 #include "chrome/browser/ash/login/screens/welcome_screen.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ash/login/oobe_metrics_helper.cc b/chrome/browser/ash/login/oobe_metrics_helper.cc
index a66a6da..46d26db 100644
--- a/chrome/browser/ash/login/oobe_metrics_helper.cc
+++ b/chrome/browser/ash/login/oobe_metrics_helper.cc
@@ -12,8 +12,8 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/auto_enrollment_check_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/demo_preferences_screen_handler.h"
diff --git a/chrome/browser/ash/login/password_change_browsertest.cc b/chrome/browser/ash/login/password_change_browsertest.cc
index c207acde..03bdfaa 100644
--- a/chrome/browser/ash/login/password_change_browsertest.cc
+++ b/chrome/browser/ash/login/password_change_browsertest.cc
@@ -28,13 +28,13 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/notifications/notification_handler.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
diff --git a/chrome/browser/ash/login/quickstart_controller.cc b/chrome/browser/ash/login/quickstart_controller.cc
index 70d64c06..1af232e 100644
--- a/chrome/browser/ash/login/quickstart_controller.cc
+++ b/chrome/browser/ash/login/quickstart_controller.cc
@@ -16,11 +16,11 @@
 #include "chrome/browser/ash/login/oobe_quick_start/oobe_quick_start_pref_names.h"
 #include "chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/add_child_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
@@ -373,7 +373,6 @@
   metrics_->RecordScreenClosed(
       QuickStartMetrics::ScreenName::kQSComplete,
       QuickStartMetrics::ScreenClosedReason::kSetupComplete);
-  metrics_.reset();
 }
 
 void QuickStartController::InitTargetDeviceBootstrapController() {
diff --git a/chrome/browser/ash/login/reporting/BUILD.gn b/chrome/browser/ash/login/reporting/BUILD.gn
index 4480cbd..df1965b 100644
--- a/chrome/browser/ash/login/reporting/BUILD.gn
+++ b/chrome/browser/ash/login/reporting/BUILD.gn
@@ -78,13 +78,13 @@
     "//chrome/browser/ash/login/app_mode/test:test_support",
     "//chrome/browser/ash/login/lock:test_support",
     "//chrome/browser/ash/login/session:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/settings",
     "//chrome/browser/ash/settings:test_support",
     "//chrome/browser/policy/messaging_layer/proto:lock_unlock_event_proto",
     "//chrome/browser/policy/messaging_layer/proto:login_logout_event_proto",
+    "//chrome/browser/ui/ash/login",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//chromeos/ash/components/dbus/session_manager",
diff --git a/chrome/browser/ash/login/reporting/DEPS b/chrome/browser/ash/login/reporting/DEPS
index 3df0d0245..027380ba 100644
--- a/chrome/browser/ash/login/reporting/DEPS
+++ b/chrome/browser/ash/login/reporting/DEPS
@@ -25,5 +25,6 @@
   "+chrome/browser/extensions/browsertest_util.h",
   "+chrome/browser/policy/messaging_layer/proto/synced",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/test/base",
 ]
diff --git a/chrome/browser/ash/login/reporting/login_logout_reporter_browsertest.cc b/chrome/browser/ash/login/reporting/login_logout_reporter_browsertest.cc
index f1d4d69..45b33ab2 100644
--- a/chrome/browser/ash/login/reporting/login_logout_reporter_browsertest.cc
+++ b/chrome/browser/ash/login/reporting/login_logout_reporter_browsertest.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/core/device_local_account_policy_service.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
@@ -34,6 +33,7 @@
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/policy/messaging_layer/proto/synced/login_logout_event.pb.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/ash/login/reset_browsertest.cc b/chrome/browser/ash/login/reset_browsertest.cc
index f55e99a..14484ff 100644
--- a/chrome/browser/ash/login/reset_browsertest.cc
+++ b/chrome/browser/ash/login/reset_browsertest.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/reset_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ash/login/saml/BUILD.gn b/chrome/browser/ash/login/saml/BUILD.gn
index b47b15b..30ddedc 100644
--- a/chrome/browser/ash/login/saml/BUILD.gn
+++ b/chrome/browser/ash/login/saml/BUILD.gn
@@ -160,7 +160,6 @@
     "//chrome/browser/ash/login/lock:test_support",
     "//chrome/browser/ash/login/lock/online_reauth",
     "//chrome/browser/ash/login/session:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users",
     "//chrome/browser/ash/net",
     "//chrome/browser/ash/policy/affiliation:test_support",
@@ -172,6 +171,7 @@
     "//chrome/browser/profiles",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/lock_screen_reauth",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/ui/webui/signin",
diff --git a/chrome/browser/ash/login/saml/DEPS b/chrome/browser/ash/login/saml/DEPS
index e34b500..e84df47a 100644
--- a/chrome/browser/ash/login/saml/DEPS
+++ b/chrome/browser/ash/login/saml/DEPS
@@ -35,6 +35,7 @@
   "+chrome/browser/policy",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/browser.h",
   "+chrome/browser/ui/webui/ash/in_session_password_change",
   "+chrome/browser/ui/webui/ash/lock_screen_reauth",
diff --git a/chrome/browser/ash/login/saml/saml_browsertest.cc b/chrome/browser/ash/login/saml/saml_browsertest.cc
index 2f9a57cc..49940841 100644
--- a/chrome/browser/ash/login/saml/saml_browsertest.cc
+++ b/chrome/browser/ash/login/saml/saml_browsertest.cc
@@ -46,7 +46,6 @@
 #include "chrome/browser/ash/login/test/scoped_policy_update.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/test_users.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/net/delay_network_call.h"
@@ -62,6 +61,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/saml_challenge_key_handler.h"
diff --git a/chrome/browser/ash/login/saml/security_token_saml_test.cc b/chrome/browser/ash/login/saml/security_token_saml_test.cc
index 0b03723..32ee9003 100644
--- a/chrome/browser/ash/login/saml/security_token_saml_test.cc
+++ b/chrome/browser/ash/login/saml/security_token_saml_test.cc
@@ -19,13 +19,13 @@
 #include "chrome/browser/ash/login/saml/test_client_cert_saml_idp_mixin.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/test_users.h"
 #include "chrome/browser/ash/net/delay_network_call.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/certificate_provider/test_certificate_provider_extension.h"
 #include "chrome/browser/policy/extension_force_install_mixin.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
diff --git a/chrome/browser/ash/login/screens/BUILD.gn b/chrome/browser/ash/login/screens/BUILD.gn
index 008f757..ae2ba72 100644
--- a/chrome/browser/ash/login/screens/BUILD.gn
+++ b/chrome/browser/ash/login/screens/BUILD.gn
@@ -177,6 +177,7 @@
     "//chrome/browser/ash/login/saml",
     "//chrome/browser/ash/login/screens/chromevox_hint",
     "//chrome/browser/ash/policy/core",
+    "//chrome/browser/ash/tpm",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/ash/keyboard",
     "//chrome/browser/ui/webui/ash/login/mojom",
@@ -255,7 +256,6 @@
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/signin",
     "//chrome/browser/ash/login/smart_lock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users",
     "//chrome/browser/ash/login/users/default_user_image",
     "//chrome/browser/ash/multidevice_setup",
@@ -344,8 +344,8 @@
     "//chrome/browser/ash/login/app_mode",
     "//chrome/browser/ash/login/lock",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/handlers",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login/testapi",
   ]
 }
@@ -480,7 +480,6 @@
     "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/login/screens/chromevox_hint",
     "//chrome/browser/ash/login/session:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/version_updater",
     "//chrome/browser/ash/net",
     "//chrome/browser/ash/policy/core:test_support",
@@ -491,6 +490,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/sync",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/ui/webui/ash/settings",
     "//chrome/browser/ui/webui/ash/system_web_dialog",
diff --git a/chrome/browser/ash/login/screens/DEPS b/chrome/browser/ash/login/screens/DEPS
index 1cdecf5..7f27de4e 100644
--- a/chrome/browser/ash/login/screens/DEPS
+++ b/chrome/browser/ash/login/screens/DEPS
@@ -42,7 +42,7 @@
   "+chrome/browser/ash/profiles",
   "+chrome/browser/ash/settings",
   "+chrome/browser/ash/system",
-  "+chrome/browser/ash/tpm_firmware_update.h",
+  "+chrome/browser/ash/tpm",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/browser_process_platform_part_ash.h",
   "+chrome/browser/browser_process_platform_part.h",
diff --git a/chrome/browser/ash/login/screens/add_child_screen.cc b/chrome/browser/ash/login/screens/add_child_screen.cc
index 6d4e3dae..7794e32 100644
--- a/chrome/browser/ash/login/screens/add_child_screen.cc
+++ b/chrome/browser/ash/login/screens/add_child_screen.cc
@@ -8,8 +8,8 @@
 
 #include "ash/public/cpp/login_screen.h"
 #include "chrome/browser/ash/login/error_screens_histogram_helper.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/add_child_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chromeos/ash/components/network/network_state.h"
diff --git a/chrome/browser/ash/login/screens/add_child_screen_browsertest.cc b/chrome/browser/ash/login/screens/add_child_screen_browsertest.cc
index 061cad5..17a1ef34 100644
--- a/chrome/browser/ash/login/screens/add_child_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/add_child_screen_browsertest.cc
@@ -15,8 +15,8 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/add_child_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_info_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/app_downloading_screen_browsertest.cc b/chrome/browser/ash/login/screens/app_downloading_screen_browsertest.cc
index 552db15..bf8e180 100644
--- a/chrome/browser/ash/login/screens/app_downloading_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/app_downloading_screen_browsertest.cc
@@ -19,10 +19,10 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/app_downloading_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
diff --git a/chrome/browser/ash/login/screens/arc_vm_data_migration_screen.cc b/chrome/browser/ash/login/screens/arc_vm_data_migration_screen.cc
index 825a4ee..2782c73 100644
--- a/chrome/browser/ash/login/screens/arc_vm_data_migration_screen.cc
+++ b/chrome/browser/ash/login/screens/arc_vm_data_migration_screen.cc
@@ -21,10 +21,10 @@
 #include "base/time/default_tick_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/arc/arc_util.h"
-#include "chrome/browser/ash/login/ui/login_feedback.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_feedback.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/spaced/spaced_client.h"
diff --git a/chrome/browser/ash/login/screens/assistant_optin_flow_screen_browsertest.cc b/chrome/browser/ash/login/screens/assistant_optin_flow_screen_browsertest.cc
index f19dae5..c24f618d 100644
--- a/chrome/browser/ash/login/screens/assistant_optin_flow_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/assistant_optin_flow_screen_browsertest.cc
@@ -23,10 +23,10 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/assistant_optin_flow_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen.cc b/chrome/browser/ash/login/screens/consolidated_consent_screen.cc
index ce1973b..f4c4a93 100644
--- a/chrome/browser/ash/login/screens/consolidated_consent_screen.cc
+++ b/chrome/browser/ash/login/screens/consolidated_consent_screen.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager_util.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
@@ -37,6 +36,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
index e23eeca..72378c23 100644
--- a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
@@ -21,14 +21,14 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
 #include "chrome/browser/ash/login/test/webview_content_extractor.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
 #include "chrome/browser/consent_auditor/consent_auditor_test_utils.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ash/login/screens/consumer_update_screen_browsertest.cc b/chrome/browser/ash/login/screens/consumer_update_screen_browsertest.cc
index f26be0b6..dbda0d2 100644
--- a/chrome/browser/ash/login/screens/consumer_update_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/consumer_update_screen_browsertest.cc
@@ -25,13 +25,13 @@
 #include "chrome/browser/ash/login/test/network_portal_detector_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/version_updater/version_updater.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/net/network_portal_detector_test_impl.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/policy/handlers/minimum_version_policy_test_helpers.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_info_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/core_oobe.cc b/chrome/browser/ash/login/screens/core_oobe.cc
index feb1258..9137bc4 100644
--- a/chrome/browser/ash/login/screens/core_oobe.cc
+++ b/chrome/browser/ash/login/screens/core_oobe.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/login/screens/core_oobe.h"
+
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/shell.h"
@@ -10,9 +11,9 @@
 #include "base/functional/callback_forward.h"
 #include "build/branding_buildflags.h"
 #include "chrome/browser/ash/login/configuration_keys.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
 #include "chrome/browser/ash/system/input_device_settings.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 #include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/common/channel_info.h"
diff --git a/chrome/browser/ash/login/screens/edu_coexistence_login_browsertest.cc b/chrome/browser/ash/login/screens/edu_coexistence_login_browsertest.cc
index daf51c6c..1f65c60 100644
--- a/chrome/browser/ash/login/screens/edu_coexistence_login_browsertest.cc
+++ b/chrome/browser/ash/login/screens/edu_coexistence_login_browsertest.cc
@@ -2,23 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/screens/edu_coexistence_login_screen.h"
-
 #include "base/functional/callback.h"
 #include "base/metrics/histogram_base.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_future.h"
+#include "chrome/browser/ash/login/screens/edu_coexistence_login_screen.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
 #include "chrome/browser/ash/login/test/wizard_controller_screen_exit_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/test_support/embedded_policy_test_server_mixin.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/edu_coexistence_login_screen.cc b/chrome/browser/ash/login/screens/edu_coexistence_login_screen.cc
index f24e364..251bd7d 100644
--- a/chrome/browser/ash/login/screens/edu_coexistence_login_screen.cc
+++ b/chrome/browser/ash/login/screens/edu_coexistence_login_screen.cc
@@ -8,12 +8,12 @@
 
 #include "chrome/browser/ash/login/oobe_screen.h"
 #include "chrome/browser/ash/login/screen_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
-#include "chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_mojo.h"
+#include "chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/signin/ash/inline_login_dialog_onboarding.h"
 #include "chromeos/ash/components/osauth/public/auth_session_storage.h"
diff --git a/chrome/browser/ash/login/screens/edu_coexistence_login_screen.h b/chrome/browser/ash/login/screens/edu_coexistence_login_screen.h
index 7ab0b850..5316b581 100644
--- a/chrome/browser/ash/login/screens/edu_coexistence_login_screen.h
+++ b/chrome/browser/ash/login/screens/edu_coexistence_login_screen.h
@@ -8,7 +8,7 @@
 #include "base/functional/callback.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ash/login/screens/base_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/signin/ash/inline_login_dialog_onboarding.h"
 
 namespace gfx {
diff --git a/chrome/browser/ash/login/screens/enable_adb_sideloading_screen.cc b/chrome/browser/ash/login/screens/enable_adb_sideloading_screen.cc
index 0dbe9be8..d53fd38 100644
--- a/chrome/browser/ash/login/screens/enable_adb_sideloading_screen.cc
+++ b/chrome/browser/ash/login/screens/enable_adb_sideloading_screen.cc
@@ -8,8 +8,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/ash/login/screens/enable_adb_sideloading_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/enable_adb_sideloading_screen_handler.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
diff --git a/chrome/browser/ash/login/screens/enable_debugging_screen.cc b/chrome/browser/ash/login/screens/enable_debugging_screen.cc
index 3bd0e59c..d369905 100644
--- a/chrome/browser/ash/login/screens/enable_debugging_screen.cc
+++ b/chrome/browser/ash/login/screens/enable_debugging_screen.cc
@@ -7,11 +7,11 @@
 #include "base/check.h"
 #include "base/check_op.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_web_dialog.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_web_dialog.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
diff --git a/chrome/browser/ash/login/screens/encryption_migration_screen.cc b/chrome/browser/ash/login/screens/encryption_migration_screen.cc
index cb7c42c..6142524 100644
--- a/chrome/browser/ash/login/screens/encryption_migration_screen.cc
+++ b/chrome/browser/ash/login/screens/encryption_migration_screen.cc
@@ -21,12 +21,12 @@
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/arc/arc_migration_constants.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_feedback.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_feedback.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power/power_policy_controller.h"
diff --git a/chrome/browser/ash/login/screens/error_screen.cc b/chrome/browser/ash/login/screens/error_screen.cc
index e1b2debe..a2f539a5 100644
--- a/chrome/browser/ash/login/screens/error_screen.cc
+++ b/chrome/browser/ash/login/screens/error_screen.cc
@@ -20,14 +20,14 @@
 #include "chrome/browser/ash/login/screens/connectivity_diagnostics_dialog.h"
 #include "chrome/browser/ash/login/signin_specifics.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/captive_portal_window_proxy.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
-#include "chrome/browser/ash/login/ui/login_web_dialog.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/captive_portal_window_proxy.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_mojo.h"
+#include "chrome/browser/ui/ash/login/login_web_dialog.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/internet/internet_detail_dialog.h"
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/family_link_notice_browsertest.cc b/chrome/browser/ash/login/screens/family_link_notice_browsertest.cc
index 2e6ab76d..a05b9594 100644
--- a/chrome/browser/ash/login/screens/family_link_notice_browsertest.cc
+++ b/chrome/browser/ash/login/screens/family_link_notice_browsertest.cc
@@ -1,21 +1,20 @@
 // Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-#include "chrome/browser/ash/login/screens/family_link_notice_screen.h"
-
 #include "ash/constants/ash_features.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
+#include "chrome/browser/ash/login/screens/family_link_notice_screen.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
 #include "chrome/browser/ash/login/test/wizard_controller_screen_exit_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/test_support/embedded_policy_test_server_mixin.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/family_link_notice_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ash/login/screens/gaia_screen.cc b/chrome/browser/ash/login/screens/gaia_screen.cc
index b99d56e6..32539a2 100644
--- a/chrome/browser/ash/login/screens/gaia_screen.cc
+++ b/chrome/browser/ash/login/screens/gaia_screen.cc
@@ -12,12 +12,12 @@
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/account_status_check_fetcher.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/util/managed_browser_utils.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
diff --git a/chrome/browser/ash/login/screens/guest_tos_screen_browsertest.cc b/chrome/browser/ash/login/screens/guest_tos_screen_browsertest.cc
index 7dcc669a..c65d72e 100644
--- a/chrome/browser/ash/login/screens/guest_tos_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/guest_tos_screen_browsertest.cc
@@ -2,19 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/ash/login/screens/guest_tos_screen.h"
+
 #include "ash/constants/ash_switches.h"
 #include "base/containers/contains.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/screens/guest_tos_screen.h"
 #include "chrome/browser/ash/login/test/fake_eula_mixin.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/webview_content_extractor.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/ash/login/guest_tos_screen_handler.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/login/screens/lacros_data_migration_screen.cc b/chrome/browser/ash/login/screens/lacros_data_migration_screen.cc
index b2fd4ec..1b6b9c2 100644
--- a/chrome/browser/ash/login/screens/lacros_data_migration_screen.cc
+++ b/chrome/browser/ash/login/screens/lacros_data_migration_screen.cc
@@ -15,10 +15,10 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/crosapi/browser_data_migrator.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/lacros_data_migration_screen_handler.h"
 #include "chrome/common/chrome_paths.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/screens/lacros_data_migration_screen_browsertest.cc b/chrome/browser/ash/login/screens/lacros_data_migration_screen_browsertest.cc
index 0f83d34..db0bde0c 100644
--- a/chrome/browser/ash/login/screens/lacros_data_migration_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/lacros_data_migration_screen_browsertest.cc
@@ -17,9 +17,9 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host_mojo.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/lacros_data_migration_screen_handler.h"
 #include "chrome/test/base/in_process_browser_test.h"
diff --git a/chrome/browser/ash/login/screens/locale_switch_notification.cc b/chrome/browser/ash/login/screens/locale_switch_notification.cc
index 8e4c6088..1f304b15 100644
--- a/chrome/browser/ash/login/screens/locale_switch_notification.cc
+++ b/chrome/browser/ash/login/screens/locale_switch_notification.cc
@@ -12,11 +12,11 @@
 #include "ash/public/cpp/notification_utils.h"
 #include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/notifications/notification_common.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/notification_display_service_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
diff --git a/chrome/browser/ash/login/screens/management_transition_screen_browsertest.cc b/chrome/browser/ash/login/screens/management_transition_screen_browsertest.cc
index fdacaff6..b35f4e5e 100644
--- a/chrome/browser/ash/login/screens/management_transition_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/management_transition_screen_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/ash/login/screens/management_transition_screen.h"
+
 #include <memory>
 #include <string>
 
@@ -18,15 +20,14 @@
 #include "chrome/browser/ash/arc/session/arc_service_launcher.h"
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/ash/arc/test/test_arc_session_manager.h"
-#include "chrome/browser/ash/login/screens/management_transition_screen.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/management_transition_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
diff --git a/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc b/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
index 57b56ebe..863b335 100644
--- a/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
@@ -26,11 +26,11 @@
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/test_support/embedded_policy_test_server_mixin.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/multidevice_setup_screen_browsertest.cc b/chrome/browser/ash/login/screens/multidevice_setup_screen_browsertest.cc
index e6b2ab8..89dcd23f 100644
--- a/chrome/browser/ash/login/screens/multidevice_setup_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/multidevice_setup_screen_browsertest.cc
@@ -14,9 +14,9 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/multidevice_setup_screen_handler.h"
 #include "chromeos/ash/services/device_sync/public/cpp/fake_device_sync_client.h"
diff --git a/chrome/browser/ash/login/screens/offline_login_screen.cc b/chrome/browser/ash/login/screens/offline_login_screen.cc
index 1ecfc9b7..c0244e15 100644
--- a/chrome/browser/ash/login/screens/offline_login_screen.cc
+++ b/chrome/browser/ash/login/screens/offline_login_screen.cc
@@ -12,12 +12,12 @@
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/helper.h"
 #include "chrome/browser/ash/login/screen_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "chrome/browser/ui/webui/ash/login/offline_login_screen_handler.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ash/login/screens/os_install_screen.cc b/chrome/browser/ash/login/screens/os_install_screen.cc
index e05d612..b47c923 100644
--- a/chrome/browser/ash/login/screens/os_install_screen.cc
+++ b/chrome/browser/ash/login/screens/os_install_screen.cc
@@ -6,7 +6,7 @@
 
 #include "ash/public/cpp/login_accelerators.h"
 #include "base/time/default_tick_clock.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/os_install_screen_handler.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 
diff --git a/chrome/browser/ash/login/screens/osauth/BUILD.gn b/chrome/browser/ash/login/screens/osauth/BUILD.gn
index f4fcabb0..7ed7dfb 100644
--- a/chrome/browser/ash/login/screens/osauth/BUILD.gn
+++ b/chrome/browser/ash/login/screens/osauth/BUILD.gn
@@ -51,8 +51,8 @@
     "//chrome/browser/ash/auth",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/quick_unlock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/cryptohome:public",
     "//chromeos/ash/components/dbus/session_manager",
@@ -67,7 +67,7 @@
 
   allow_circular_includes_from = [
     "//chrome/browser/ash/login",
-    "//chrome/browser/ash/login/ui",
+    "//chrome/browser/ui/ash/login",
   ]
 }
 
@@ -94,10 +94,10 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/test_support",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/ui/webui/ash/login/osauth",
     "//chrome/test:test_support_ui",
diff --git a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
index 4b20250..f70a0d36 100644
--- a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_screen_browsertest.cc
@@ -20,9 +20,9 @@
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
 #include "chrome/browser/ash/login/test/wizard_controller_screen_exit_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/enter_old_password_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen_browsertest.cc b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen_browsertest.cc
index aacdcbda..4854045 100644
--- a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen_browsertest.cc
@@ -21,8 +21,8 @@
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/wizard_controller_screen_exit_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_setup_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chromeos/ash/components/cryptohome/constants.h"
diff --git a/chrome/browser/ash/login/screens/osauth/local_password_setup_screen_browsertest.cc b/chrome/browser/ash/login/screens/osauth/local_password_setup_screen_browsertest.cc
index 85e4234..a395fbc 100644
--- a/chrome/browser/ash/login/screens/osauth/local_password_setup_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/osauth/local_password_setup_screen_browsertest.cc
@@ -19,8 +19,8 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/local_password_setup_handler.h"
 #include "chromeos/ash/components/cryptohome/auth_factor.h"
 #include "chromeos/ash/components/cryptohome/common_types.h"
diff --git a/chrome/browser/ash/login/screens/osauth/recovery_eligibility_screen_browsertest.cc b/chrome/browser/ash/login/screens/osauth/recovery_eligibility_screen_browsertest.cc
index 37fb3d5..b6b4a1a4 100644
--- a/chrome/browser/ash/login/screens/osauth/recovery_eligibility_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/osauth/recovery_eligibility_screen_browsertest.cc
@@ -25,12 +25,12 @@
 #include "chrome/browser/ash/login/test/scoped_policy_update.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
 #include "chrome/browser/ash/login/test/wizard_controller_screen_exit_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/test_support/embedded_policy_test_server_mixin.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/recovery_eligibility_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
diff --git a/chrome/browser/ash/login/screens/pin_setup_screen_browsertest.cc b/chrome/browser/ash/login/screens/pin_setup_screen_browsertest.cc
index 4cc0075..df9ca682 100644
--- a/chrome/browser/ash/login/screens/pin_setup_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/pin_setup_screen_browsertest.cc
@@ -22,9 +22,9 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/pin_setup_screen_handler.h"
 #include "chromeos/ash/components/cryptohome/constants.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
diff --git a/chrome/browser/ash/login/screens/quick_start_screen.cc b/chrome/browser/ash/login/screens/quick_start_screen.cc
index a4c65f5..dd99a26 100644
--- a/chrome/browser/ash/login/screens/quick_start_screen.cc
+++ b/chrome/browser/ash/login/screens/quick_start_screen.cc
@@ -8,8 +8,8 @@
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/qr_code.h"
 #include "chrome/browser/ash/login/quickstart_controller.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h"
 #include "chromeos/ash/components/osauth/public/auth_session_storage.h"
 
diff --git a/chrome/browser/ash/login/screens/quick_start_screen.h b/chrome/browser/ash/login/screens/quick_start_screen.h
index 932fa68..36e2b83c 100644
--- a/chrome/browser/ash/login/screens/quick_start_screen.h
+++ b/chrome/browser/ash/login/screens/quick_start_screen.h
@@ -11,7 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/quickstart_controller.h"
 #include "chrome/browser/ash/login/screens/base_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/screens/quick_start_screen_browsertest.cc b/chrome/browser/ash/login/screens/quick_start_screen_browsertest.cc
index f55f761..716f371 100644
--- a/chrome/browser/ash/login/screens/quick_start_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/quick_start_screen_browsertest.cc
@@ -24,8 +24,8 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/network_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc b/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc
index 609879ab..5e75f90 100644
--- a/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/recommend_apps_screen_browsertest.cc
@@ -24,10 +24,10 @@
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/recommend_apps_screen_handler.h"
 #include "components/account_id/account_id.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/screens/reset_screen.cc b/chrome/browser/ash/login/screens/reset_screen.cc
index cec049f..3b303f1 100644
--- a/chrome/browser/ash/login/screens/reset_screen.cc
+++ b/chrome/browser/ash/login/screens/reset_screen.cc
@@ -17,10 +17,10 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/values.h"
 #include "build/branding_buildflags.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/reset_screen_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
diff --git a/chrome/browser/ash/login/screens/reset_screen.h b/chrome/browser/ash/login/screens/reset_screen.h
index 37ea6f03..9c5e72f 100644
--- a/chrome/browser/ash/login/screens/reset_screen.h
+++ b/chrome/browser/ash/login/screens/reset_screen.h
@@ -15,7 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/help_app_launcher.h"
 #include "chrome/browser/ash/login/screens/base_screen.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
 
 class PrefRegistrySimple;
diff --git a/chrome/browser/ash/login/screens/saml_confirm_password_screen.cc b/chrome/browser/ash/login/screens/saml_confirm_password_screen.cc
index e3b97b7..fcdae43 100644
--- a/chrome/browser/ash/login/screens/saml_confirm_password_screen.cc
+++ b/chrome/browser/ash/login/screens/saml_confirm_password_screen.cc
@@ -9,7 +9,7 @@
 #include "base/containers/contains.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/screens/base_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/check_passwords_against_cryptohome_helper.h"
 #include "chrome/browser/ui/webui/ash/login/saml_confirm_password_handler.h"
 #include "chromeos/ash/components/login/auth/public/auth_types.h"
diff --git a/chrome/browser/ash/login/screens/signin_fatal_error_screen.cc b/chrome/browser/ash/login/screens/signin_fatal_error_screen.cc
index 06056fc..0f44f69 100644
--- a/chrome/browser/ash/login/screens/signin_fatal_error_screen.cc
+++ b/chrome/browser/ash/login/screens/signin_fatal_error_screen.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ash/login/screens/signin_fatal_error_screen.h"
 
 #include "base/values.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/signin_fatal_error_screen_handler.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/screens/sync_consent_browsertest.cc b/chrome/browser/ash/login/screens/sync_consent_browsertest.cc
index 4688c841..0f2aa6e 100644
--- a/chrome/browser/ash/login/screens/sync_consent_browsertest.cc
+++ b/chrome/browser/ash/login/screens/sync_consent_browsertest.cc
@@ -27,13 +27,13 @@
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/test_condition_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/marketing_opt_in_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/sync_consent_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/terms_of_service_screen_browsertest.cc b/chrome/browser/ash/login/screens/terms_of_service_screen_browsertest.cc
index 63830dc..2dccb3b 100644
--- a/chrome/browser/ash/login/screens/terms_of_service_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/terms_of_service_screen_browsertest.cc
@@ -24,14 +24,14 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/policy/test_support/embedded_policy_test_server_mixin.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/ash/login/family_link_notice_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/update_required_screen.cc b/chrome/browser/ash/login/screens/update_required_screen.cc
index 78c3c2e..7c36526 100644
--- a/chrome/browser/ash/login/screens/update_required_screen.cc
+++ b/chrome/browser/ash/login/screens/update_required_screen.cc
@@ -15,11 +15,11 @@
 #include "base/time/default_clock.h"
 #include "chrome/browser/ash/login/error_screens_histogram_helper.h"
 #include "chrome/browser/ash/login/helper.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/update_required_screen_handler.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
diff --git a/chrome/browser/ash/login/screens/update_required_screen_browsertest.cc b/chrome/browser/ash/login/screens/update_required_screen_browsertest.cc
index ad15f33..6668ac8 100644
--- a/chrome/browser/ash/login/screens/update_required_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/update_required_screen_browsertest.cc
@@ -21,13 +21,13 @@
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/version_updater/version_updater.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/net/network_portal_detector_test_impl.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/policy/handlers/minimum_version_policy_test_helpers.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
diff --git a/chrome/browser/ash/login/screens/update_screen_browsertest.cc b/chrome/browser/ash/login/screens/update_screen_browsertest.cc
index 8f9ccdd..9f380240 100644
--- a/chrome/browser/ash/login/screens/update_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/update_screen_browsertest.cc
@@ -24,10 +24,10 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/version_updater/version_updater.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/ash/login/update_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/user_creation_screen.cc b/chrome/browser/ash/login/screens/user_creation_screen.cc
index 811dce6..720cffff 100644
--- a/chrome/browser/ash/login/screens/user_creation_screen.cc
+++ b/chrome/browser/ash/login/screens/user_creation_screen.cc
@@ -6,8 +6,8 @@
 
 #include "ash/public/cpp/login_screen.h"
 #include "chrome/browser/ash/login/error_screens_histogram_helper.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
diff --git a/chrome/browser/ash/login/screens/user_creation_screen_browsertest.cc b/chrome/browser/ash/login/screens/user_creation_screen_browsertest.cc
index 58b8579..ee678dd 100644
--- a/chrome/browser/ash/login/screens/user_creation_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/user_creation_screen_browsertest.cc
@@ -17,8 +17,8 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_info_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/user_selection_screen.cc b/chrome/browser/ash/login/screens/user_selection_screen.cc
index c1d5a83..2803aa7 100644
--- a/chrome/browser/ash/login/screens/user_selection_screen.cc
+++ b/chrome/browser/ash/login/screens/user_selection_screen.cc
@@ -37,13 +37,13 @@
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/ash/login/reauth_stats.h"
 #include "chrome/browser/ash/login/smart_lock/smart_lock_service.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/default_user_image/default_user_images.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/system/system_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
 #include "chrome/browser/ui/webui/ash/login/l10n_util.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc b/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
index 5405b556..fdfcece 100644
--- a/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
@@ -23,10 +23,10 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/welcome_screen.cc b/chrome/browser/ash/login/screens/welcome_screen.cc
index 7bf7c13..459d32c 100644
--- a/chrome/browser/ash/login/screens/welcome_screen.cc
+++ b/chrome/browser/ash/login/screens/welcome_screen.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/ui/input_events_blocker.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
 #include "chrome/browser/ash/system/timezone_resolver_manager.h"
@@ -34,6 +33,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/input_events_blocker.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
 #include "chrome/browser/ui/webui/ash/login/l10n_util.h"
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
diff --git a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
index 1746620..d369dd4 100644
--- a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
@@ -32,10 +32,10 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/enable_debugging_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
diff --git a/chrome/browser/ash/login/session/BUILD.gn b/chrome/browser/ash/login/session/BUILD.gn
index e1ce880..d726c2e 100644
--- a/chrome/browser/ash/login/session/BUILD.gn
+++ b/chrome/browser/ash/login/session/BUILD.gn
@@ -64,6 +64,7 @@
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/settings",
+    "//chrome/browser/ash/tpm",
     "//chrome/browser/ash/u2f",
     "//chrome/browser/google",
     "//chrome/browser/scalable_iph:scalable_iph_factory",
@@ -169,9 +170,9 @@
     "//base",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/lock:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/google",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common:non_code_constants",
     "//chrome/test:test_support_ui",
diff --git a/chrome/browser/ash/login/session/DEPS b/chrome/browser/ash/login/session/DEPS
index 9d2c54d5..c75a178 100644
--- a/chrome/browser/ash/login/session/DEPS
+++ b/chrome/browser/ash/login/session/DEPS
@@ -53,7 +53,7 @@
   "+chrome/browser/ash/sparky/sparky_manager_service_factory.h",
   "+chrome/browser/ash/system_web_apps/apps/help_app",
   "+chrome/browser/ash/tether",
-  "+chrome/browser/ash/tpm_firmware_update_notification.h",
+  "+chrome/browser/ash/tpm",
   "+chrome/browser/ash/u2f",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/browser_process_platform_part_ash.h",
diff --git a/chrome/browser/ash/login/session/chrome_session_manager.cc b/chrome/browser/ash/login/session/chrome_session_manager.cc
index b393bfd..72d7b67 100644
--- a/chrome/browser/ash/login/session/chrome_session_manager.cc
+++ b/chrome/browser/ash/login/session/chrome_session_manager.cc
@@ -38,7 +38,6 @@
 #include "chrome/browser/ash/login/session/session_length_limiter.h"
 #include "chrome/browser/ash/login/session/user_session_initializer.h"
 #include "chrome/browser/ash/login/session/user_session_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/ash/profiles/signin_profile_handler.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_ash.h"
@@ -46,6 +45,7 @@
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/arc_vm_data_migration_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/lacros_data_backward_migration_screen_handler.h"
diff --git a/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc b/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
index 526394c..6517de6 100644
--- a/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
+++ b/chrome/browser/ash/login/session/chrome_session_manager_browsertest.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index a82bc7f8..e5cd31c 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -86,8 +86,6 @@
 #include "chrome/browser/ash/login/signin/offline_signin_limiter_factory.h"
 #include "chrome/browser/ash/login/signin/token_handle_fetcher.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/input_events_blocker.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/net/alwayson_vpn_pre_connect_url_allowlist_service.h"
 #include "chrome/browser/ash/net/alwayson_vpn_pre_connect_url_allowlist_service_factory.h"
@@ -101,7 +99,7 @@
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/ash/system_web_apps/apps/help_app/help_app_notification_controller.h"
 #include "chrome/browser/ash/tether/tether_service.h"
-#include "chrome/browser/ash/tpm_firmware_update_notification.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update_notification.h"
 #include "chrome/browser/ash/u2f/u2f_notification.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_ash.h"
@@ -119,6 +117,8 @@
 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
 #include "chrome/browser/sync/desk_sync_service_factory.h"
 #include "chrome/browser/trusted_vault/trusted_vault_service_factory.h"
+#include "chrome/browser/ui/ash/login/input_events_blocker.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/system/system_tray_client_impl.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/common/channel_info.h"
diff --git a/chrome/browser/ash/login/shill_profile_loading_browsertest.cc b/chrome/browser/ash/login/shill_profile_loading_browsertest.cc
index 24cf8dcb..49d5473c4 100644
--- a/chrome/browser/ash/login/shill_profile_loading_browsertest.cc
+++ b/chrome/browser/ash/login/shill_profile_loading_browsertest.cc
@@ -24,7 +24,7 @@
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h"
diff --git a/chrome/browser/ash/login/signin/BUILD.gn b/chrome/browser/ash/login/signin/BUILD.gn
index e153d9e8..7562765 100644
--- a/chrome/browser/ash/login/signin/BUILD.gn
+++ b/chrome/browser/ash/login/signin/BUILD.gn
@@ -118,10 +118,10 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/extensions:test_support",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common:constants",
     "//chrome/common:non_code_constants",
diff --git a/chrome/browser/ash/login/signin/DEPS b/chrome/browser/ash/login/signin/DEPS
index d7bf2d4..f444ce2b2 100644
--- a/chrome/browser/ash/login/signin/DEPS
+++ b/chrome/browser/ash/login/signin/DEPS
@@ -29,6 +29,7 @@
   "+chrome/browser/signin",
   "+chrome/browser/supervised_user",
   "+chrome/browser/sync",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/browser.h",
   "+chrome/browser/ui/browser_tabstrip.h",
diff --git a/chrome/browser/ash/login/signin/device_id_browsertest.cc b/chrome/browser/ash/login/signin/device_id_browsertest.cc
index 131fff4..af985fa3 100644
--- a/chrome/browser/ash/login/signin/device_id_browsertest.cc
+++ b/chrome/browser/ash/login/signin/device_id_browsertest.cc
@@ -18,11 +18,11 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/signin/chrome_device_id_helper.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chrome/common/chrome_switches.h"
diff --git a/chrome/browser/ash/login/signin/oauth2_browsertest.cc b/chrome/browser/ash/login/signin/oauth2_browsertest.cc
index b27a37d..3430f769 100644
--- a/chrome/browser/ash/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/ash/login/signin/oauth2_browsertest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/ash/login/test/network_portal_detector_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
@@ -36,6 +35,7 @@
 #include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
diff --git a/chrome/browser/ash/login/startup_utils.cc b/chrome/browser/ash/login/startup_utils.cc
index 37f9dc8..250cad0 100644
--- a/chrome/browser/ash/login/startup_utils.cc
+++ b/chrome/browser/ash/login/startup_utils.cc
@@ -26,10 +26,10 @@
 #include "chrome/browser/ash/login/oobe_configuration.h"
 #include "chrome/browser/ash/login/oobe_metrics_helper.h"
 #include "chrome/browser/ash/login/oobe_quick_start/oobe_quick_start_pref_names.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_common.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_token_provider.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_common.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/dbus/oobe_config/oobe_configuration_client.h"
diff --git a/chrome/browser/ash/login/test/BUILD.gn b/chrome/browser/ash/login/test/BUILD.gn
index 224c28b..3d0595a4 100644
--- a/chrome/browser/ash/login/test/BUILD.gn
+++ b/chrome/browser/ash/login/test/BUILD.gn
@@ -132,7 +132,6 @@
     "//chrome/browser/ash/extensions",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/session:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/net",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/policy/enrollment",
diff --git a/chrome/browser/ash/login/test/auth_ui_utils.cc b/chrome/browser/ash/login/test/auth_ui_utils.cc
index e669274..c4bdafa0 100644
--- a/chrome/browser/ash/login/test/auth_ui_utils.cc
+++ b/chrome/browser/ash/login/test/auth_ui_utils.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
 #include "chrome/browser/ash/login/test/test_condition_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/cryptohome_recovery_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/enter_old_password_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
diff --git a/chrome/browser/ash/login/test/gaia_page_event_waiter.cc b/chrome/browser/ash/login/test/gaia_page_event_waiter.cc
index 606486df..efe16d5 100644
--- a/chrome/browser/ash/login/test/gaia_page_event_waiter.cc
+++ b/chrome/browser/ash/login/test/gaia_page_event_waiter.cc
@@ -7,7 +7,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/login/test/js_checker.cc b/chrome/browser/ash/login/test/js_checker.cc
index fd519a9..03341eb 100644
--- a/chrome/browser/ash/login/test/js_checker.cc
+++ b/chrome/browser/ash/login/test/js_checker.cc
@@ -15,7 +15,7 @@
 #include "base/json/string_escape.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/isolated_world_ids.h"
diff --git a/chrome/browser/ash/login/test/login_manager_mixin.cc b/chrome/browser/ash/login/test/login_manager_mixin.cc
index c7c996037..3402eb3 100644
--- a/chrome/browser/ash/login/test/login_manager_mixin.cc
+++ b/chrome/browser/ash/login/test/login_manager_mixin.cc
@@ -23,9 +23,9 @@
 #include "chrome/browser/ash/login/test/profile_prepared_waiter.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chromeos/ash/components/login/auth/auth_status_consumer.h"
 #include "chromeos/ash/components/login/auth/public/auth_types.h"
diff --git a/chrome/browser/ash/login/test/offline_login_test_mixin.cc b/chrome/browser/ash/login/test/offline_login_test_mixin.cc
index a8df451..c831d1c 100644
--- a/chrome/browser/ash/login/test/offline_login_test_mixin.cc
+++ b/chrome/browser/ash/login/test/offline_login_test_mixin.cc
@@ -10,11 +10,11 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/test_condition_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/ash/settings/device_settings_provider.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/offline_login_screen_handler.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
diff --git a/chrome/browser/ash/login/test/oobe_auth_page_waiter.cc b/chrome/browser/ash/login/test/oobe_auth_page_waiter.cc
index 4441f858..7ee0184e 100644
--- a/chrome/browser/ash/login/test/oobe_auth_page_waiter.cc
+++ b/chrome/browser/ash/login/test/oobe_auth_page_waiter.cc
@@ -6,7 +6,7 @@
 
 #include "base/strings/string_util.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "content/public/test/browser_test_utils.h"
 
diff --git a/chrome/browser/ash/login/test/oobe_base_test.cc b/chrome/browser/ash/login/test/oobe_base_test.cc
index d6e09aae0b..f73cfbe 100644
--- a/chrome/browser/ash/login/test/oobe_base_test.cc
+++ b/chrome/browser/ash/login/test/oobe_base_test.cc
@@ -19,10 +19,10 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/test_condition_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/ash/login/update_screen_handler.h"
diff --git a/chrome/browser/ash/login/test/oobe_screen_exit_waiter.cc b/chrome/browser/ash/login/test/oobe_screen_exit_waiter.cc
index 48ac150..e5e05b9 100644
--- a/chrome/browser/ash/login/test/oobe_screen_exit_waiter.cc
+++ b/chrome/browser/ash/login/test/oobe_screen_exit_waiter.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "base/run_loop.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/test/oobe_screen_waiter.cc b/chrome/browser/ash/login/test/oobe_screen_waiter.cc
index 3548dd77..823d6c2 100644
--- a/chrome/browser/ash/login/test/oobe_screen_waiter.cc
+++ b/chrome/browser/ash/login/test/oobe_screen_waiter.cc
@@ -6,8 +6,8 @@
 
 #include "base/run_loop.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/view.h"
diff --git a/chrome/browser/ash/login/test/oobe_window_visibility_waiter.cc b/chrome/browser/ash/login/test/oobe_window_visibility_waiter.cc
index 1910b44..220ba2a 100644
--- a/chrome/browser/ash/login/test/oobe_window_visibility_waiter.cc
+++ b/chrome/browser/ash/login/test/oobe_window_visibility_waiter.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ash/login/test/oobe_window_visibility_waiter.h"
 
 #include "base/run_loop.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/gfx/native_widget_types.h"
 
diff --git a/chrome/browser/ash/login/test/user_adding_screen_utils.cc b/chrome/browser/ash/login/test/user_adding_screen_utils.cc
index 479e844..6b8a24e 100644
--- a/chrome/browser/ash/login/test/user_adding_screen_utils.cc
+++ b/chrome/browser/ash/login/test/user_adding_screen_utils.cc
@@ -5,8 +5,8 @@
 #include "chrome/browser/ash/login/test/user_adding_screen_utils.h"
 
 #include "base/run_loop.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chromeos/ash/experiences/login/login_screen_shown_observer.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/login/test/webview_content_extractor.cc b/chrome/browser/ash/login/test/webview_content_extractor.cc
index 63ac4c09..53d6c7c 100644
--- a/chrome/browser/ash/login/test/webview_content_extractor.cc
+++ b/chrome/browser/ash/login/test/webview_content_extractor.cc
@@ -9,7 +9,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/uuid.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
diff --git a/chrome/browser/ash/login/ui/BUILD.gn b/chrome/browser/ash/login/ui/BUILD.gn
deleted file mode 100644
index c3791a77..0000000
--- a/chrome/browser/ash/login/ui/BUILD.gn
+++ /dev/null
@@ -1,275 +0,0 @@
-# Copyright 2024 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/chromeos/ui_mode.gni")
-
-assert(is_chromeos_ash)
-
-static_library("ui") {
-  sources = [
-    "captive_portal_view.cc",
-    "captive_portal_view.h",
-    "captive_portal_window_proxy.cc",
-    "captive_portal_window_proxy.h",
-    "input_events_blocker.cc",
-    "input_events_blocker.h",
-    "kiosk_app_menu_controller.cc",
-    "kiosk_app_menu_controller.h",
-    "login_display_host.cc",
-    "login_display_host.h",
-    "login_display_host_common.cc",
-    "login_display_host_common.h",
-    "login_display_host_mojo.cc",
-    "login_display_host_mojo.h",
-    "login_display_host_webui.cc",
-    "login_display_host_webui.h",
-    "login_feedback.cc",
-    "login_feedback.h",
-    "login_ui_pref_controller.cc",
-    "login_ui_pref_controller.h",
-    "login_web_dialog.cc",
-    "login_web_dialog.h",
-    "oobe_dialog_size_utils.cc",
-    "oobe_dialog_size_utils.h",
-    "oobe_dialog_util_impl.cc",
-    "oobe_dialog_util_impl.h",
-    "oobe_ui_dialog_delegate.cc",
-    "oobe_ui_dialog_delegate.h",
-    "signin_ui.h",
-    "simple_web_view_dialog.cc",
-    "simple_web_view_dialog.h",
-    "user_adding_screen.cc",
-    "user_adding_screen.h",
-    "user_adding_screen_input_methods_controller.cc",
-    "user_adding_screen_input_methods_controller.h",
-    "webui_login_view.cc",
-    "webui_login_view.h",
-  ]
-
-  public_deps = [
-    "//ash",
-    "//ash/public/cpp",
-    "//base",
-    "//chrome/browser:browser_public_dependencies",
-    "//chrome/browser:primitives",
-    "//chrome/browser/ash/customization",
-    "//chrome/browser/ash/login/oobe_quick_start",
-    "//chrome/browser/ui:browser_list",
-    "//chrome/browser/ui/ash/keyboard",
-    "//chromeos/ash/components/audio",
-    "//chromeos/ash/components/dbus/session_manager",
-    "//chromeos/ash/components/login/auth",
-    "//chromeos/ash/components/login/auth/public:authpublic",
-    "//chromeos/ash/components/login/auth/public:challenge_response_key",
-    "//components/account_id",
-    "//components/keep_alive_registry",
-    "//components/login",
-    "//components/prefs",
-    "//components/session_manager/core",
-    "//components/user_manager",
-    "//components/user_manager:common",
-    "//components/web_modal",
-    "//content/public/browser",
-    "//ui/base",
-    "//ui/base/ime/ash",
-    "//ui/display",
-    "//ui/events",
-    "//ui/events/devices",
-    "//ui/gfx",
-    "//ui/gfx/geometry",
-    "//ui/views",
-    "//ui/views/controls/webview",
-    "//ui/web_dialogs",
-    "//url",
-  ]
-
-  deps = [
-    "//ash/constants",
-    "//base:i18n",
-    "//chrome/app:command_ids",
-    "//chrome/app:generated_resources",
-    "//chrome/app/theme:theme_resources",
-    "//chrome/browser:browser_process",
-    "//chrome/browser:resources",
-    "//chrome/browser/ash/accessibility",
-    "//chrome/browser/ash/arc",
-    "//chrome/browser/ash/arc/optin",
-    "//chrome/browser/ash/attestation",
-    "//chrome/browser/ash/base",
-    "//chrome/browser/ash/boot_times_recorder",
-    "//chrome/browser/ash/login/quick_unlock",
-    "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/nearby",
-    "//chrome/browser/ash/net",
-    "//chrome/browser/ash/policy/core",
-    "//chrome/browser/ash/policy/enrollment",
-    "//chrome/browser/ash/policy/handlers",
-    "//chrome/browser/ash/policy/remote_commands/crd",
-    "//chrome/browser/ash/profiles",
-    "//chrome/browser/ash/settings",
-    "//chrome/browser/ash/system",
-    "//chrome/browser/profiles:profile",
-    "//chrome/browser/safe_browsing",
-    "//chrome/browser/themes",
-    "//chrome/browser/ui/autofill",
-    "//chrome/browser/ui/content_settings",
-    "//chrome/browser/ui/views/toolbar",
-    "//chrome/browser/ui/webui/ash/diagnostics_dialog",
-    "//chrome/browser/ui/webui/ash/internet",
-    "//chrome/common:constants",
-    "//chrome/common:non_code_constants",
-    "//chromeos/ash/components/attestation",
-    "//chromeos/ash/components/browser_context_helper",
-    "//chromeos/ash/components/dbus/userdataauth",
-    "//chromeos/ash/components/geolocation",
-    "//chromeos/ash/components/install_attributes",
-    "//chromeos/ash/components/language_preferences",
-    "//chromeos/ash/components/login/login_state",
-    "//chromeos/ash/components/network",
-    "//chromeos/ash/components/osauth/public",
-    "//chromeos/ash/components/settings",
-    "//chromeos/ash/components/timezone",
-    "//components/captive_portal/core",
-    "//components/constrained_window",
-    "//components/content_settings/core/common",
-    "//components/language/core/browser",
-    "//components/language/core/common",
-    "//components/omnibox/browser:location_bar",
-    "//components/password_manager/core/browser",
-    "//components/session_manager:base",
-    "//components/startup_metric_utils",
-    "//components/strings:components_strings",
-    "//content/public/common",
-    "//extensions:extensions_browser_resources",
-    "//extensions/browser",
-    "//extensions/browser/api/feedback_private",
-    "//extensions/common",
-    "//extensions/common:mojom",
-    "//google_apis",
-    "//ipc:message_support",
-    "//services/audio/public/cpp",
-    "//third_party/blink/public/common:headers",
-    "//ui/accessibility:ax_base",
-    "//ui/aura",
-    "//ui/base:features",
-    "//ui/color:color_headers",
-    "//ui/compositor",
-    "//ui/gfx",
-    "//ui/gfx/geometry",
-  ]
-
-  allow_circular_includes_from = [
-    "//chrome/browser/ash/arc",
-    "//chrome/browser/ash/arc/optin",
-    "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/net",
-    "//chrome/browser/ash/policy/enrollment",
-    "//chrome/browser/ash/policy/handlers",
-    "//chrome/browser/ash/policy/remote_commands/crd",
-    "//chrome/browser/ash/system",
-  ]
-}
-
-static_library("test_support") {
-  testonly = true
-
-  sources = [
-    "fake_login_display_host.cc",
-    "fake_login_display_host.h",
-    "mock_login_display_host.cc",
-    "mock_login_display_host.h",
-    "mock_signin_ui.cc",
-    "mock_signin_ui.h",
-  ]
-
-  public_deps = [
-    ":ui",
-    "//ash/public/cpp",
-    "//base",
-    "//chrome/browser/ash/app_mode",
-    "//chrome/browser/ash/login",
-    "//chromeos/ash/components/login/auth/public:authpublic",
-    "//components/login",
-    "//components/prefs",
-    "//components/user_manager:common",
-    "//testing/gmock",
-  ]
-
-  deps = [
-    "//chrome/browser/ui",
-    "//chrome/browser/ui/webui/ash/login",
-    "//components/session_manager/core",
-  ]
-}
-
-source_set("browser_tests") {
-  testonly = true
-
-  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-
-  sources = [
-    "captive_portal_window_browsertest.cc",
-    "login_feedback_browsertest.cc",
-    "login_ui_pref_controller_browsertest.cc",
-    "login_web_dialog_browsertest.cc",
-    "simple_web_view_dialog_browsertest.cc",
-    "user_adding_screen_browsertest.cc",
-  ]
-
-  deps = [
-    ":ui",
-    "//ash/constants",
-    "//ash/public/cpp",
-    "//ash/webui/os_feedback_ui:url_constants",
-    "//base",
-    "//base/test:test_support",
-    "//chrome/browser:browser_process",
-    "//chrome/browser/ash/login:test_support",
-    "//chrome/browser/ash/login/lock",
-    "//chrome/browser/ash/login/lock:test_support",
-    "//chrome/browser/ash/login/screens",
-    "//chrome/browser/ash/net",
-    "//chrome/browser/ash/policy/core:test_support",
-    "//chrome/browser/profiles:profile",
-    "//chrome/browser/ui",
-    "//chrome/browser/ui/webui/ash/os_feedback_dialog",
-    "//chrome/browser/ui/webui/ash/system_web_dialog",
-    "//chrome/common:constants",
-    "//chrome/test:test_support",
-    "//chrome/test:test_support_ui",
-    "//chromeos/ash/components/dbus/shill",
-    "//chromeos/ash/components/network:test_support",
-    "//components/policy:generated",
-    "//components/policy:policy_code_generate",
-    "//components/policy/proto",
-    "//components/prefs",
-    "//components/prefs:test_support",
-    "//components/session_manager/core",
-    "//components/user_manager",
-    "//content/public/browser",
-    "//content/test:test_support",
-    "//net:test_support",
-    "//testing/gtest",
-    "//ui/aura",
-    "//ui/events:test_support",
-    "//ui/views",
-    "//ui/views:test_support",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-
-  sources = [ "oobe_dialog_size_utils_unittest.cc" ]
-
-  deps = [
-    ":ui",
-    "//base/test:test_support",
-    "//testing/gtest",
-    "//ui/gfx/geometry",
-
-    # Tests from subdirectories:
-    "login_screen_extension_ui:unit_tests",
-  ]
-}
diff --git a/chrome/browser/ash/login/ui/DEPS b/chrome/browser/ash/login/ui/DEPS
deleted file mode 100644
index ba20fbd1..0000000
--- a/chrome/browser/ash/login/ui/DEPS
+++ /dev/null
@@ -1,95 +0,0 @@
-include_rules = [
-  # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for
-  # details.
-  "-chrome",
-
-  # This directory is in //chrome, which violates the rule above. Allow this
-  # directory to #include its own files.
-  "+chrome/browser/ash/login/ui",
-
-  # Existing dependencies within //chrome. There is an active effort to
-  # refactor //chrome/browser/ash to break these dependencies; see b/332804822.
-  # Whenever possible, avoid adding new //chrome dependencies to this list.
-  #
-  # Files residing in certain directories (e.g., //chrome/browser) are listed
-  # individually. Other dependencies within //chrome are listed on a per-
-  # directory basis. See //tools/chromeos/gen_deps.sh for details.
-  "+chrome/app",
-  "+chrome/browser/ash/accessibility",
-  "+chrome/browser/ash/app_mode",
-  "+chrome/browser/ash/attestation",
-  "+chrome/browser/ash/auth",
-  "+chrome/browser/ash/base",
-  "+chrome/browser/ash/boot_times_recorder",
-  "+chrome/browser/ash/customization",
-  "+chrome/browser/ash/extensions/login_screen_ui",
-  "+chrome/browser/ash/first_run",
-  "+chrome/browser/ash/login",
-  "+chrome/browser/ash/nearby",
-  "+chrome/browser/ash/net",
-  "+chrome/browser/ash/policy/core",
-  "+chrome/browser/ash/policy/enrollment",
-  "+chrome/browser/ash/profiles",
-  "+chrome/browser/ash/settings",
-  "+chrome/browser/ash/system",
-  "+chrome/browser/ash/tpm_firmware_update.h",
-  "+chrome/browser/browser_process.h",
-  "+chrome/browser/browser_process_platform_part.h",
-  "+chrome/browser/certificate_provider",
-  "+chrome/browser/command_updater_delegate.h",
-  "+chrome/browser/command_updater_impl.h",
-  "+chrome/browser/lifetime",
-  "+chrome/browser/media/webrtc",
-  "+chrome/browser/password_manager",
-  "+chrome/browser/profiles",
-  "+chrome/browser/renderer_preferences_util.h",
-  "+chrome/browser/safe_browsing",
-  "+chrome/browser/sessions",
-  "+chrome/browser/signin",
-  "+chrome/browser/ssl",
-  "+chrome/browser/themes",
-  "+chrome/browser/ui/ash",
-  "+chrome/browser/ui/autofill",
-  "+chrome/browser/ui/browser_dialogs.h",
-  "+chrome/browser/ui/browser_finder.h",
-  "+chrome/browser/ui/browser.h",
-  "+chrome/browser/ui/browser_list.h",
-  "+chrome/browser/ui/browser_list_observer.h",
-  "+chrome/browser/ui/browser_window.h",
-  "+chrome/browser/ui/chrome_pages.h",
-  "+chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h",
-  "+chrome/browser/ui/content_settings",
-  "+chrome/browser/ui/login",
-  "+chrome/browser/ui/toolbar",
-  "+chrome/browser/ui/views/location_bar/location_bar_view.h",
-  "+chrome/browser/ui/views/toolbar/reload_button.h",
-  "+chrome/browser/ui/view_ids.h",
-  "+chrome/browser/ui/webui/ash/diagnostics_dialog",
-  "+chrome/browser/ui/webui/ash/internet",
-  "+chrome/browser/ui/webui/ash/login",
-  "+chrome/browser/ui/webui/ash/os_feedback_dialog",
-  "+chrome/browser/ui/webui/ash/system_web_dialog/system_web_dialog_delegate.h",
-  "+chrome/browser/ui/webui/chrome_web_contents_handler.h",
-  "+chrome/browser/ui/webui/feedback",
-  "+chrome/common/channel_info.h",
-  "+chrome/common/chrome_constants.h",
-  "+chrome/common/chrome_switches.h",
-  "+chrome/common/pref_names.h",
-  "+chrome/grit",
-  "+chrome/test/base",
-  "+chrome/test/views",
-]
-
-specific_include_rules = {
-  "input_events_blocker\.cc": [
-    "+ash/shell.h",
-  ],
-  # TODO(jdufault): Deprecate. https://crbug.com/792654
-  "login_display_host_webui\.cc": [
-    "+ash/accessibility/focus_ring_controller.h",
-    "+ash/shell.h",
-  ],
-  "login_display_host_mojo\.cc": [
-    "+ash/shell.h",
-  ],
-}
diff --git a/chrome/browser/ash/login/ui/OWNERS b/chrome/browser/ash/login/ui/OWNERS
deleted file mode 100644
index cb01c7bb..0000000
--- a/chrome/browser/ash/login/ui/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file login_display_host*.*=set noparent
-per-file login_display_host*.*=file://ash/login/LOGIN_LOCK_OWNERS
-
-per-file kiosk_app*=file://chromeos/components/kiosk/OWNERS
diff --git a/chrome/browser/ash/login/user_flags_login_browsertest.cc b/chrome/browser/ash/login/user_flags_login_browsertest.cc
index 0ed083a6..24de0ee 100644
--- a/chrome/browser/ash/login/user_flags_login_browsertest.cc
+++ b/chrome/browser/ash/login/user_flags_login_browsertest.cc
@@ -8,9 +8,9 @@
 #include "chrome/browser/ash/login/session/user_session_manager_test_api.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/settings/about_flags.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
diff --git a/chrome/browser/ash/login/users/BUILD.gn b/chrome/browser/ash/login/users/BUILD.gn
index 908d007d..7adc0982 100644
--- a/chrome/browser/ash/login/users/BUILD.gn
+++ b/chrome/browser/ash/login/users/BUILD.gn
@@ -130,13 +130,13 @@
     "//base",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/ownership",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/policy/external_data:test_support",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common:constants",
     "//chrome/test:test_support_ui",
diff --git a/chrome/browser/ash/login/users/DEPS b/chrome/browser/ash/login/users/DEPS
index 411550a..d367d501 100644
--- a/chrome/browser/ash/login/users/DEPS
+++ b/chrome/browser/ash/login/users/DEPS
@@ -34,6 +34,7 @@
   "+chrome/browser/prefs",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/browser_finder.h",
   "+chrome/browser/ui/browser_window.h",
   "+chrome/browser/ui/chrome_select_file_policy.h",
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index 097f41f..2b7cedf 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -276,7 +276,10 @@
   if (!user || user->GetType() != user_manager::UserType::kPublicAccount) {
     return;
   }
-  UpdatePublicAccountDisplayName(user_id);
+  auto display_name = GetDisplayName(user_id);
+  if (display_name) {
+    SaveUserDisplayName(user->GetAccountId(), *display_name);
+  }
 }
 
 void ChromeUserManagerImpl::OnDeviceLocalAccountsChanged() {
@@ -309,8 +312,20 @@
       owner_email, std::string() /* id */, AccountType::UNKNOWN);
   SetOwnerId(owner_account_id);
 
-  bool changed = UpdateAndCleanUpDeviceLocalAccounts(
-      policy::GetDeviceLocalAccounts(cros_settings()));
+  auto device_local_accounts = policy::GetDeviceLocalAccounts(cros_settings());
+  std::vector<DeviceLocalAccountInfo> device_local_account_info_list;
+  for (const auto& account : device_local_accounts) {
+    DeviceLocalAccountInfo info(
+        account.user_id,
+        *chrome_user_manager_util::DeviceLocalAccountTypeToUserType(
+            account.type));
+    if (info.type == user_manager::UserType::kPublicAccount) {
+      info.display_name = GetDisplayName(info.user_id);
+    }
+    device_local_account_info_list.push_back(std::move(info));
+  }
+  bool changed =
+      UpdateAndCleanUpDeviceLocalAccounts(device_local_account_info_list);
 
   // Remove ephemeral regular users (except the owner) when on the login screen.
   if (!IsUserLoggedIn()) {
@@ -348,8 +363,7 @@
   UserManagerBase::RemoveNonCryptohomeData(account_id);
 }
 
-void ChromeUserManagerImpl::
-    CleanUpDeviceLocalAccountNonCryptohomeDataPendingRemoval() {
+void ChromeUserManagerImpl::RemovePendingDeviceLocalAccount() {
   PrefService* local_state = GetLocalState();
   const std::string device_local_account_pending_data_removal =
       local_state->GetString(prefs::kDeviceLocalAccountPendingDataRemoval);
@@ -360,56 +374,17 @@
     return;
   }
 
-  RemoveNonCryptohomeData(
-      AccountId::FromUserEmail(device_local_account_pending_data_removal));
+  RemoveUserFromListImpl(
+      AccountId::FromUserEmail(device_local_account_pending_data_removal),
+      user_manager::UserRemovalReason::DEVICE_LOCAL_ACCOUNT_UPDATED,
+      /*trigger_cryptohome_removal=*/false);
   local_state->ClearPref(prefs::kDeviceLocalAccountPendingDataRemoval);
 }
 
-void ChromeUserManagerImpl::CleanUpDeviceLocalAccountNonCryptohomeData(
-    const std::vector<std::string>& old_device_local_accounts) {
-  std::set<std::string> users;
-  for (user_manager::UserList::const_iterator it = users_.begin();
-       it != users_.end(); ++it) {
-    users.insert((*it)->GetAccountId().GetUserEmail());
-  }
-
-  // If the user is logged into a device local account that has been removed
-  // from the user list, mark the account's data as pending removal after
-  // logout.
-  const user_manager::User* const active_user = GetActiveUser();
-  if (active_user && active_user->IsDeviceLocalAccount()) {
-    const std::string active_user_id =
-        active_user->GetAccountId().GetUserEmail();
-    if (users.find(active_user_id) == users.end()) {
-      GetLocalState()->SetString(prefs::kDeviceLocalAccountPendingDataRemoval,
-                                 active_user_id);
-      users.insert(active_user_id);
-    }
-  }
-
-  // Remove the data belonging to any other device local accounts that are no
-  // longer found on the user list.
-  for (std::vector<std::string>::const_iterator it =
-           old_device_local_accounts.begin();
-       it != old_device_local_accounts.end(); ++it) {
-    if (users.find(*it) == users.end()) {
-      RemoveNonCryptohomeData(AccountId::FromUserEmail(*it));
-    }
-  }
-}
-
 bool ChromeUserManagerImpl::UpdateAndCleanUpDeviceLocalAccounts(
-    const std::vector<policy::DeviceLocalAccount>& device_local_accounts) {
+    const std::vector<DeviceLocalAccountInfo>& device_local_accounts) {
   // Try to remove any device local account data marked as pending removal.
-  CleanUpDeviceLocalAccountNonCryptohomeDataPendingRemoval();
-
-  // Get the current list of device local accounts.
-  std::vector<std::string> old_accounts;
-  for (user_manager::User* user : users_) {
-    if (user->IsDeviceLocalAccount()) {
-      old_accounts.push_back(user->GetAccountId().GetUserEmail());
-    }
-  }
+  RemovePendingDeviceLocalAccount();
 
   // Persist the new list of device local accounts in a pref. These accounts
   // will be loaded in LoadDeviceLocalAccounts() on the next reboot regardless
@@ -423,55 +398,85 @@
   }
 
   // If the list of device local accounts has not changed, return.
-  if (device_local_accounts.size() == old_accounts.size()) {
+  {
     bool changed = false;
-    for (size_t i = 0; i < device_local_accounts.size(); ++i) {
-      if (device_local_accounts[i].user_id != old_accounts[i]) {
+    size_t i = 0;
+    for (const user_manager::User* user : users_) {
+      if (!user->IsDeviceLocalAccount()) {
+        continue;
+      }
+      if (i >= device_local_accounts.size()) {
         changed = true;
         break;
       }
+      if (user->GetAccountId().GetUserEmail() !=
+              device_local_accounts[i].user_id ||
+          user->GetType() != device_local_accounts[i].type) {
+        changed = true;
+        break;
+      }
+      ++i;
     }
-    if (!changed) {
+    if (i == device_local_accounts.size() && !changed) {
       return false;
     }
   }
 
   // Remove the old device local accounts from the user list.
-  // Take snapshot because DeleteUser will update |user_|.
-  std::vector<raw_ptr<user_manager::User, VectorExperimental>> users = users_;
+  // Take snapshot because RemoveUserFromListImpl will update |user_|.
+  std::vector<user_manager::User*> users(users_.begin(), users_.end());
   for (user_manager::User* user : users) {
-    if (user->IsDeviceLocalAccount()) {
-      if (user != GetActiveUser()) {
-        DeleteUser(user);
-      } else {
-        std::erase(users_, user);
-      }
+    if (!user->IsDeviceLocalAccount()) {
+      // Non device local account is not a target to be removed.
+      continue;
     }
+    if (base::ranges::any_of(
+            device_local_accounts, [user](const DeviceLocalAccountInfo& info) {
+              return info.user_id == user->GetAccountId().GetUserEmail() &&
+                     info.type == user->GetType();
+            })) {
+      // The account exists in new device local accounts. Do not remove.
+      continue;
+    }
+    if (user == GetActiveUser()) {
+      // This user is active, so keep the instance. Instead, mark it as
+      // pending removal, so it will be removed in the next turn.
+      GetLocalState()->SetString(prefs::kDeviceLocalAccountPendingDataRemoval,
+                                 user->GetAccountId().GetUserEmail());
+      std::erase(users_, user);
+      continue;
+    }
+
+    // Remove the instance.
+    RemoveUserFromListImpl(
+        user->GetAccountId(),
+        user_manager::UserRemovalReason::DEVICE_LOCAL_ACCOUNT_UPDATED,
+        /*trigger_cryptohome_removal=*/false);
   }
 
   // Add the new device local accounts to the front of the user list.
-  user_manager::User* const active_user = GetActiveUser();
-  const bool is_device_local_account_session =
-      active_user && active_user->IsDeviceLocalAccount();
-  for (const policy::DeviceLocalAccount& account :
-       base::Reversed(device_local_accounts)) {
-    if (is_device_local_account_session &&
-        AccountId::FromUserEmail(account.user_id) ==
-            active_user->GetAccountId()) {
-      users_.insert(users_.begin(), active_user);
+  for (size_t i = 0; i < device_local_accounts.size(); ++i) {
+    const DeviceLocalAccountInfo& account = device_local_accounts[i];
+    auto iter = std::find_if(users_.begin() + i, users_.end(),
+                             [&account](const user_manager::User* user) {
+                               return user->GetAccountId().GetUserEmail() ==
+                                          account.user_id &&
+                                      user->GetType() == account.type;
+                             });
+    if (iter != users_.end()) {
+      // Found the instance. Rotate the `users_` to place the found user at
+      // the i-th position.
+      std::rotate(users_.begin() + i, iter, iter + 1);
     } else {
-      auto user_type =
-          chrome_user_manager_util::DeviceLocalAccountTypeToUserType(
-              account.type);
-      CHECK(user_type.has_value());
+      // Not found so create an instance.
       // Using `new` to access a non-public constructor.
       user_storage_.push_back(base::WrapUnique(new user_manager::User(
-          AccountId::FromUserEmail(account.user_id), *user_type)));
-      users_.insert(users_.begin(), user_storage_.back().get());
+          AccountId::FromUserEmail(account.user_id), account.type)));
+      users_.insert(users_.begin() + i, user_storage_.back().get());
     }
-    if (account.type == policy::DeviceLocalAccountType::kPublicSession ||
-        account.type == policy::DeviceLocalAccountType::kSamlPublicSession) {
-      UpdatePublicAccountDisplayName(account.user_id);
+    if (account.display_name) {
+      SaveUserDisplayName(AccountId::FromUserEmail(account.user_id),
+                          *account.display_name);
     }
   }
 
@@ -479,27 +484,22 @@
     observer.OnDeviceLocalUserListUpdated();
   }
 
-  // Remove data belonging to device local accounts that are no longer found on
-  // the user list.
-  CleanUpDeviceLocalAccountNonCryptohomeData(old_accounts);
-
   return true;
 }
 
-void ChromeUserManagerImpl::UpdatePublicAccountDisplayName(
-    const std::string& user_id) {
-  std::string display_name;
-
-  if (device_local_account_policy_service_) {
-    policy::DeviceLocalAccountPolicyBroker* broker =
-        device_local_account_policy_service_->GetBrokerForUser(user_id);
-    if (broker) {
-      display_name = broker->GetDisplayName();
-      // Set or clear the display name.
-      SaveUserDisplayName(AccountId::FromUserEmail(user_id),
-                          base::UTF8ToUTF16(display_name));
-    }
+std::optional<std::u16string> ChromeUserManagerImpl::GetDisplayName(
+    std::string_view user_id) {
+  if (!device_local_account_policy_service_) {
+    return std::nullopt;
   }
+
+  policy::DeviceLocalAccountPolicyBroker* broker =
+      device_local_account_policy_service_->GetBrokerForUser(user_id);
+  if (!broker) {
+    return std::nullopt;
+  }
+
+  return base::UTF8ToUTF16(broker->GetDisplayName());
 }
 
 void ChromeUserManagerImpl::OnMinimumVersionStateChanged() {
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.h b/chrome/browser/ash/login/users/chrome_user_manager_impl.h
index 9010c7a8..7bf33f6 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.h
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.h
@@ -75,14 +75,7 @@
 
   // If data for a device local account is marked as pending removal and the
   // user is no longer logged into that account, removes the data.
-  void CleanUpDeviceLocalAccountNonCryptohomeDataPendingRemoval();
-
-  // Removes data belonging to device local accounts that are no longer found on
-  // the user list. If the user is currently logged into one of these accounts,
-  // the data for that account is not removed immediately but marked as pending
-  // removal after logout.
-  void CleanUpDeviceLocalAccountNonCryptohomeData(
-      const std::vector<std::string>& old_device_local_accounts);
+  void RemovePendingDeviceLocalAccount();
 
   // Replaces the list of device local accounts with those found in
   // `device_local_accounts`. Ensures that data belonging to accounts no longer
@@ -90,17 +83,17 @@
   // Device local accounts are defined by policy. This method is called whenever
   // an updated list of device local accounts is received from policy.
   bool UpdateAndCleanUpDeviceLocalAccounts(
-      const std::vector<policy::DeviceLocalAccount>& device_local_accounts);
-
-  // Updates the display name for public account `username` from policy settings
-  // associated with that username.
-  void UpdatePublicAccountDisplayName(const std::string& user_id);
+      const std::vector<DeviceLocalAccountInfo>& device_local_accounts);
 
   // Update the number of users.
   void UpdateNumberOfUsers();
 
   void UpdateOwnerId();
 
+  // Returns the display name taken from policy, expected to be used for
+  // public accounts.
+  std::optional<std::u16string> GetDisplayName(std::string_view user_id);
+
   // Interface to device-local account definitions and associated policy.
   raw_ptr<policy::DeviceLocalAccountPolicyService>
       device_local_account_policy_service_;
diff --git a/chrome/browser/ash/login/users/wallpaper_policy_browsertest.cc b/chrome/browser/ash/login/users/wallpaper_policy_browsertest.cc
index 0d275c6..ad6bfac5 100644
--- a/chrome/browser/ash/login/users/wallpaper_policy_browsertest.cc
+++ b/chrome/browser/ash/login/users/wallpaper_policy_browsertest.cc
@@ -32,13 +32,13 @@
 #include "chrome/browser/ash/login/startup_utils.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
 #include "chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h"
 #include "chrome/browser/ash/policy/external_data/cloud_external_data_manager_base_test_util.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
diff --git a/chrome/browser/ash/login/webview_login_browsertest.cc b/chrome/browser/ash/login/webview_login_browsertest.cc
index 6a1fc96..7088916e 100644
--- a/chrome/browser/ash/login/webview_login_browsertest.cc
+++ b/chrome/browser/ash/login/webview_login_browsertest.cc
@@ -53,7 +53,6 @@
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/user_auth_config.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -67,6 +66,7 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ssl/ssl_client_certificate_selector.h"
 #include "chrome/browser/sync/sync_service_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index 7822cf4..69ed8b9 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -132,7 +132,6 @@
 // LINT.ThenChange(//tools/metrics/histograms/metadata/oobe/histograms.xml)
 #include "chrome/browser/ash/login/session/user_session_manager.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.h"
 #include "chrome/browser/ash/net/delay_network_call.h"
@@ -152,6 +151,7 @@
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/system/system_tray_client_impl.h"
 #include "chrome/browser/ui/webui/ash/login/add_child_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/ai_intro_screen_handler.h"
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index 391d08f..c3de1b3 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -68,8 +68,6 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/net/network_portal_detector_test_impl.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/enrollment/auto_enrollment_client.h"
@@ -86,6 +84,8 @@
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/display_size_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
diff --git a/chrome/browser/ash/login/wizard_controller_unittest.cc b/chrome/browser/ash/login/wizard_controller_unittest.cc
index 9737089..9ad55c7 100644
--- a/chrome/browser/ash/login/wizard_controller_unittest.cc
+++ b/chrome/browser/ash/login/wizard_controller_unittest.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/ash/input_method/input_method_configuration.h"
 #include "chrome/browser/ash/login/enrollment/mock_enrollment_launcher.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/net/network_portal_detector_test_impl.h"
 #include "chrome/browser/ash/net/rollback_network_config/fake_rollback_network_config.h"
@@ -33,6 +32,7 @@
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client_test_helper.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 #include "chrome/browser/ui/ash/wallpaper/test_wallpaper_controller.h"
 #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
 #include "chrome/browser/ui/webui/ash/login/demo_preferences_screen_handler.h"
diff --git a/chrome/browser/ash/mahi/DEPS b/chrome/browser/ash/mahi/DEPS
index 0ff4afd..dddbdf6 100644
--- a/chrome/browser/ash/mahi/DEPS
+++ b/chrome/browser/ash/mahi/DEPS
@@ -19,6 +19,7 @@
   "+chrome/browser/feedback/show_feedback_page.h",
   "+chrome/browser/history/history_service_factory.h",
   "+chrome/browser/manta",
+  "+chrome/browser/policy/profile_policy_connector.h",
   "+chrome/browser/profiles",
   "+chrome/browser/ui/browser_finder.h",
   "+chrome/browser/ui/chrome_pages.h",
diff --git a/chrome/browser/ash/mahi/mahi_availability.cc b/chrome/browser/ash/mahi/mahi_availability.cc
index fe14a869..1966602 100644
--- a/chrome/browser/ash/mahi/mahi_availability.cc
+++ b/chrome/browser/ash/mahi/mahi_availability.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/manta/manta_service_factory.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
@@ -40,6 +41,12 @@
       return false;
     }
 
+    // Controls for managed users.
+    if (profile->GetProfilePolicyConnector()->IsManaged() &&
+        !chromeos::features::IsMahiManagedEnabled()) {
+      return false;
+    }
+
     // Guest session is not allowed when not in demo mode.
     if (profile->IsGuestSession()) {
       return false;
diff --git a/chrome/browser/ash/net/DEPS b/chrome/browser/ash/net/DEPS
index baefc950..b451658 100644
--- a/chrome/browser/ash/net/DEPS
+++ b/chrome/browser/ash/net/DEPS
@@ -35,6 +35,7 @@
   "+chrome/browser/prefs",
   "+chrome/browser/profiles",
   "+chrome/browser/sync",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/network",
   "+chrome/browser/ui/browser_finder.h",
   "+chrome/browser/ui/browser.h",
diff --git a/chrome/browser/ash/net/apn_migrator_unittest.cc b/chrome/browser/ash/net/apn_migrator_unittest.cc
index 1c8e4210..9edd8f3 100644
--- a/chrome/browser/ash/net/apn_migrator_unittest.cc
+++ b/chrome/browser/ash/net/apn_migrator_unittest.cc
@@ -289,7 +289,8 @@
 
 TEST_F(ApnMigratorTest, AlreadyMigratedNetworks) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kApnRevamp);
+  scoped_feature_list.InitAndDisableFeature(
+      features::kAllowApnModificationPolicy);
 
   const std::string cellular_service_path_1 =
       AddTestCellularDeviceAndService(kCellularName1, kTestCellularPath1,
diff --git a/chrome/browser/ash/net/system_proxy_manager.cc b/chrome/browser/ash/net/system_proxy_manager.cc
index bc44974..399f0ad 100644
--- a/chrome/browser/ash/net/system_proxy_manager.cc
+++ b/chrome/browser/ash/net/system_proxy_manager.cc
@@ -16,10 +16,10 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/values.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/notifications/request_system_proxy_credentials_view.h"
 #include "chrome/browser/ash/notifications/system_proxy_notification.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
diff --git a/chrome/browser/ash/policy/arc/BUILD.gn b/chrome/browser/ash/policy/arc/BUILD.gn
index 492484c6..461905b 100644
--- a/chrome/browser/ash/policy/arc/BUILD.gn
+++ b/chrome/browser/ash/policy/arc/BUILD.gn
@@ -72,11 +72,11 @@
     "//chrome/browser/ash/arc",
     "//chrome/browser/ash/arc/session",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/affiliation:test_support",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chromeos/ash/components/settings",
     "//components/policy/proto",
     "//content/test:test_support",
diff --git a/chrome/browser/ash/policy/arc/DEPS b/chrome/browser/ash/policy/arc/DEPS
index 97a9946..351cbd0 100644
--- a/chrome/browser/ash/policy/arc/DEPS
+++ b/chrome/browser/ash/policy/arc/DEPS
@@ -16,10 +16,10 @@
   # directory basis. See //tools/chromeos/gen_deps.sh for details.
   "+chrome/browser/ash/arc",
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/policy/affiliation",
   "+chrome/browser/ash/policy/core",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/lifetime",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
 ]
diff --git a/chrome/browser/ash/policy/arc/unaffiliated_arc_allowed_browsertest.cc b/chrome/browser/ash/policy/arc/unaffiliated_arc_allowed_browsertest.cc
index 4e6c741..23014ba1 100644
--- a/chrome/browser/ash/policy/arc/unaffiliated_arc_allowed_browsertest.cc
+++ b/chrome/browser/ash/policy/arc/unaffiliated_arc_allowed_browsertest.cc
@@ -12,13 +12,13 @@
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/ash/login/test/cryptohome_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_mixin.h"
 #include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
diff --git a/chrome/browser/ash/policy/core/BUILD.gn b/chrome/browser/ash/policy/core/BUILD.gn
index c516ae8..73f5c0b 100644
--- a/chrome/browser/ash/policy/core/BUILD.gn
+++ b/chrome/browser/ash/policy/core/BUILD.gn
@@ -85,6 +85,7 @@
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/settings",
     "//chrome/browser/ash/system",
+    "//chrome/browser/ash/tpm",
     "//chrome/browser/profiles:profile",
     "//chrome/common",
     "//chrome/common:chrome_features",
@@ -149,6 +150,7 @@
     "//chrome/browser/ash/policy/rsu",
     "//chrome/browser/ash/settings",
     "//chrome/browser/ash/system",
+    "//chrome/browser/ash/tpm",
   ]
 }
 
@@ -330,7 +332,6 @@
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/session:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users",
     "//chrome/browser/ash/login/users/avatar:test_support",
     "//chrome/browser/ash/policy/external_data:test_support",
@@ -346,6 +347,7 @@
     "//chrome/browser/policy:test_support",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/web_applications",
     "//chrome/common",
diff --git a/chrome/browser/ash/policy/core/DEPS b/chrome/browser/ash/policy/core/DEPS
index 3644fd5..3ea0034 100644
--- a/chrome/browser/ash/policy/core/DEPS
+++ b/chrome/browser/ash/policy/core/DEPS
@@ -45,7 +45,7 @@
   "+chrome/browser/ash/session_length_limiter.h",
   "+chrome/browser/ash/settings",
   "+chrome/browser/ash/system",
-  "+chrome/browser/ash/tpm_firmware_update.h",
+  "+chrome/browser/ash/tpm",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/browser_process_platform_part_ash.h",
   "+chrome/browser/browser_process_platform_part.h",
@@ -67,6 +67,7 @@
   "+chrome/browser/prefs",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/browser_commands.h",
   "+chrome/browser/ui/browser.h",
   "+chrome/browser/ui/browser_list.h",
diff --git a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
index 80d0cb2..386418b 100644
--- a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
@@ -71,7 +71,6 @@
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
 #include "chrome/browser/ash/login/test/webview_content_extractor.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/avatar/user_image_manager_test_util.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager_impl.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
@@ -103,6 +102,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_list_observer.h"
diff --git a/chrome/browser/ash/policy/core/device_policy_decoder.cc b/chrome/browser/ash/policy/core/device_policy_decoder.cc
index 7b5b4316..30573fe 100644
--- a/chrome/browser/ash/policy/core/device_policy_decoder.cc
+++ b/chrome/browser/ash/policy/core/device_policy_decoder.cc
@@ -22,7 +22,7 @@
 #include "base/types/expected_macros.h"
 #include "chrome/browser/ash/policy/handlers/device_dlc_predownload_list_policy_handler.h"
 #include "chrome/browser/ash/policy/off_hours/off_hours_proto_parser.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
diff --git a/chrome/browser/ash/policy/core/shutdown_policy_browsertest.cc b/chrome/browser/ash/policy/core/shutdown_policy_browsertest.cc
index a3fbd572..28323d089 100644
--- a/chrome/browser/ash/policy/core/shutdown_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/core/shutdown_policy_browsertest.cc
@@ -20,12 +20,12 @@
 #include "chrome/browser/ash/login/lock/screen_locker_tester.h"
 #include "chrome/browser/ash/login/test/login_or_lock_screen_visible_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
diff --git a/chrome/browser/ash/policy/display/BUILD.gn b/chrome/browser/ash/policy/display/BUILD.gn
index f323664..2420d8c 100644
--- a/chrome/browser/ash/policy/display/BUILD.gn
+++ b/chrome/browser/ash/policy/display/BUILD.gn
@@ -46,8 +46,8 @@
     "//base",
     "//chrome/browser",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core:test_support",
+    "//chrome/browser/ui/ash/login",
     "//chrome/test:test_support_ui",
     "//chromeos/ash/components/dbus/session_manager",
     "//chromeos/ash/components/settings",
diff --git a/chrome/browser/ash/policy/display/DEPS b/chrome/browser/ash/policy/display/DEPS
index 68c8c96..dd49f14f 100644
--- a/chrome/browser/ash/policy/display/DEPS
+++ b/chrome/browser/ash/policy/display/DEPS
@@ -15,8 +15,8 @@
   # individually. Other dependencies within //chrome are listed on a per-
   # directory basis. See //tools/chromeos/gen_deps.sh for details.
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/policy/core",
   "+chrome/browser/lifetime",
+  "+chrome/browser/ui/ash/login",
   "+chrome/test/base",
 ]
diff --git a/chrome/browser/ash/policy/display/device_display_cros_browser_test.cc b/chrome/browser/ash/policy/display/device_display_cros_browser_test.cc
index d472d87..65a91ae 100644
--- a/chrome/browser/ash/policy/display/device_display_cros_browser_test.cc
+++ b/chrome/browser/ash/policy/display/device_display_cros_browser_test.cc
@@ -7,8 +7,8 @@
 #include "ash/display/display_configuration_controller.h"
 #include "ash/shell.h"
 #include "base/task/single_thread_task_runner.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
diff --git a/chrome/browser/ash/policy/display/display_resolution_handler_browsertest.cc b/chrome/browser/ash/policy/display/display_resolution_handler_browsertest.cc
index afb6893..cbf13990 100644
--- a/chrome/browser/ash/policy/display/display_resolution_handler_browsertest.cc
+++ b/chrome/browser/ash/policy/display/display_resolution_handler_browsertest.cc
@@ -14,10 +14,10 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
 #include "chrome/browser/ash/policy/display/device_display_cros_browser_test.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
diff --git a/chrome/browser/ash/policy/enrollment/BUILD.gn b/chrome/browser/ash/policy/enrollment/BUILD.gn
index 47d46bf..2d7d2f4 100644
--- a/chrome/browser/ash/policy/enrollment/BUILD.gn
+++ b/chrome/browser/ash/policy/enrollment/BUILD.gn
@@ -59,6 +59,7 @@
     "//chrome/browser:browser_public_dependencies",
     "//chrome/browser/ash/policy/dev_mode",
     "//chrome/browser/ash/policy/enrollment/psm",
+    "//chrome/browser/ash/tpm",
     "//chrome/browser/profiles:profile",
     "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/dbus",
@@ -71,8 +72,10 @@
     "//chromeos/ash/components/system",
   ]
 
-  allow_circular_includes_from =
-      [ "//chrome/browser/ash/policy/enrollment/psm" ]
+  allow_circular_includes_from = [
+    "//chrome/browser/ash/policy/enrollment/psm",
+    "//chrome/browser/ash/tpm",
+  ]
 }
 
 static_library("test_support") {
@@ -126,12 +129,12 @@
     "//build:branding_buildflags",
     "//chrome/browser",
     "//chrome/browser:browser_process",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/enrollment/psm",
     "//chrome/browser/ash/policy/enrollment/psm:test_support",
     "//chrome/browser/ash/policy/server_backed_state",
     "//chrome/browser/ash/settings",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/common:constants",
     "//chrome/test:test_support",
     "//chromeos/ash/components/dbus/attestation",
diff --git a/chrome/browser/ash/policy/enrollment/DEPS b/chrome/browser/ash/policy/enrollment/DEPS
index a7ad630b..7668bbd 100644
--- a/chrome/browser/ash/policy/enrollment/DEPS
+++ b/chrome/browser/ash/policy/enrollment/DEPS
@@ -27,6 +27,7 @@
   "+chrome/browser/policy",
   "+chrome/browser/prefs",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/common/chrome_content_client.h",
   "+chrome/common/pref_names.h",
   "+chrome/test/base",
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_config.cc b/chrome/browser/ash/policy/enrollment/enrollment_config.cc
index 9d0234129..66d3820 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_config.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_config.cc
@@ -17,12 +17,12 @@
 #include "chrome/browser/ash/login/configuration_keys.h"
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/oobe_configuration.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_token_provider.h"
 #include "chrome/browser/ash/policy/server_backed_state/server_backed_device_state.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "chromeos/ash/components/system/statistics_provider.h"
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc b/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
index 57cefb1c..1bb5e9b 100644
--- a/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/enrollment_config_unittest.cc
@@ -12,11 +12,11 @@
 #include "chrome/browser/ash/login/configuration_keys.h"
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/oobe_configuration.h"
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_test_helper.h"
 #include "chrome/browser/ash/policy/server_backed_state/server_backed_device_state.h"
 #include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
diff --git a/chrome/browser/ash/policy/handlers/BUILD.gn b/chrome/browser/ash/policy/handlers/BUILD.gn
index 339b1e2..8e703c3 100644
--- a/chrome/browser/ash/policy/handlers/BUILD.gn
+++ b/chrome/browser/ash/policy/handlers/BUILD.gn
@@ -71,6 +71,7 @@
     "//chrome/browser/ash/policy/skyvault",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/settings",
+    "//chrome/browser/ash/tpm",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/web_applications",
     "//chrome/common:chrome_features",
@@ -186,6 +187,7 @@
     "//chrome/browser/ash/plugin_vm",
     "//chrome/browser/ash/settings",
     "//chrome/browser/ash/settings:test_support",
+    "//chrome/browser/ash/tpm",
     "//chrome/browser/extensions",
     "//chrome/browser/ui",
     "//chrome/browser/ui/ash/shelf",
@@ -270,7 +272,6 @@
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/session:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/core:test_support",
@@ -279,6 +280,7 @@
     "//chrome/browser/ash/system",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common:constants",
     "//chrome/test:test_support",
diff --git a/chrome/browser/ash/policy/handlers/DEPS b/chrome/browser/ash/policy/handlers/DEPS
index 83d0c1f1..4b1a4610 100644
--- a/chrome/browser/ash/policy/handlers/DEPS
+++ b/chrome/browser/ash/policy/handlers/DEPS
@@ -33,7 +33,7 @@
   "+chrome/browser/ash/profiles",
   "+chrome/browser/ash/settings",
   "+chrome/browser/ash/system",
-  "+chrome/browser/ash/tpm_firmware_update.h",
+  "+chrome/browser/ash/tpm",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/browser_process_platform_part.h",
   "+chrome/browser/extensions/policy_handlers.h",
diff --git a/chrome/browser/ash/policy/handlers/device_system_use_24hour_clock_browsertest.cc b/chrome/browser/ash/policy/handlers/device_system_use_24hour_clock_browsertest.cc
index 1c834cc6..0518880 100644
--- a/chrome/browser/ash/policy/handlers/device_system_use_24hour_clock_browsertest.cc
+++ b/chrome/browser/ash/policy/handlers/device_system_use_24hour_clock_browsertest.cc
@@ -9,12 +9,12 @@
 #include "base/location.h"
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_runner.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/system/system_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
index 96953fd9..db8b53d 100644
--- a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
+++ b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_browsertest.cc
@@ -34,7 +34,6 @@
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
@@ -48,6 +47,7 @@
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_delegate_impl.cc b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_delegate_impl.cc
index e174c4b2..7965aff 100644
--- a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_delegate_impl.cc
+++ b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_delegate_impl.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/screens/base_screen.h"
 #include "chrome/browser/ash/login/screens/update_required_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/browser_process.h"
@@ -20,6 +19,7 @@
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/update_required_screen_handler.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ash/policy/handlers/site_isolation_flag_handling_browsertest.cc b/chrome/browser/ash/policy/handlers/site_isolation_flag_handling_browsertest.cc
index b121a9a..0a8de9ebb 100644
--- a/chrome/browser/ash/policy/handlers/site_isolation_flag_handling_browsertest.cc
+++ b/chrome/browser/ash/policy/handlers/site_isolation_flag_handling_browsertest.cc
@@ -18,11 +18,11 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager_impl.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/site_isolation/about_flags.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
diff --git a/chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler.cc b/chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler.cc
index 3d9893a..a1947aa2 100644
--- a/chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler.cc
+++ b/chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler.cc
@@ -11,7 +11,7 @@
 #include "base/timer/timer.h"
 #include "base/values.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler_unittest.cc b/chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler_unittest.cc
index caf13fff..0c10f152 100644
--- a/chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler_unittest.cc
+++ b/chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler_unittest.cc
@@ -15,7 +15,7 @@
 #include "base/values.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
diff --git a/chrome/browser/ash/policy/login/BUILD.gn b/chrome/browser/ash/policy/login/BUILD.gn
index c854e9e6..21179b9 100644
--- a/chrome/browser/ash/policy/login/BUILD.gn
+++ b/chrome/browser/ash/policy/login/BUILD.gn
@@ -89,13 +89,13 @@
     "//chrome/browser/ash/accessibility",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/extensions",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
     "//chrome/browser/ui/ash/keyboard",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common:constants",
     "//chrome/common:non_code_constants",
diff --git a/chrome/browser/ash/policy/login/DEPS b/chrome/browser/ash/policy/login/DEPS
index 04776a46..69e06a8 100644
--- a/chrome/browser/ash/policy/login/DEPS
+++ b/chrome/browser/ash/policy/login/DEPS
@@ -30,6 +30,7 @@
   "+chrome/browser/policy",
   "+chrome/browser/profiles",
   "+chrome/browser/ui/ash/keyboard",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/browser.h",
   "+chrome/browser/ui/browser_list.h",
   "+chrome/browser/ui/browser_window.h",
diff --git a/chrome/browser/ash/policy/login/blocking_login_browsertest.cc b/chrome/browser/ash/policy/login/blocking_login_browsertest.cc
index d7899c6ad..75b0bbd31 100644
--- a/chrome/browser/ash/policy/login/blocking_login_browsertest.cc
+++ b/chrome/browser/ash/policy/login/blocking_login_browsertest.cc
@@ -13,12 +13,12 @@
 #include "base/strings/string_util.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
diff --git a/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc b/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
index 96688a6..c0b1393 100644
--- a/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
@@ -21,11 +21,11 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/reset_screen_handler.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ash/policy/login/login_policy_test_base.cc b/chrome/browser/ash/policy/login/login_policy_test_base.cc
index 45c3e1cb..f57edf4a 100644
--- a/chrome/browser/ash/policy/login/login_policy_test_base.cc
+++ b/chrome/browser/ash/policy/login/login_policy_test_base.cc
@@ -11,10 +11,10 @@
 #include "base/values.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/user_policy_test_helper.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
diff --git a/chrome/browser/ash/policy/networking/BUILD.gn b/chrome/browser/ash/policy/networking/BUILD.gn
index 3e1d4fc..fa57bb8 100644
--- a/chrome/browser/ash/policy/networking/BUILD.gn
+++ b/chrome/browser/ash/policy/networking/BUILD.gn
@@ -74,7 +74,6 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/policy/login:browser_tests",
     "//chrome/browser/ash/policy/login:test_support",
@@ -83,6 +82,7 @@
     "//chrome/browser/policy:onc",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/network",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common:constants",
diff --git a/chrome/browser/ash/policy/networking/DEPS b/chrome/browser/ash/policy/networking/DEPS
index 8121a5a..41ef344 100644
--- a/chrome/browser/ash/policy/networking/DEPS
+++ b/chrome/browser/ash/policy/networking/DEPS
@@ -24,6 +24,7 @@
   "+chrome/browser/net",
   "+chrome/browser/policy",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/network",
   "+chrome/browser/ui/browser.h",
   "+chrome/browser/ui/browser_list.h",
diff --git a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
index a8585db..79b2a28 100644
--- a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
+++ b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/policy/login/login_policy_test_base.h"
@@ -41,6 +40,7 @@
 #include "chrome/browser/policy/networking/user_network_configuration_updater_factory.h"
 #include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
diff --git a/chrome/browser/ash/policy/remote_commands/DEPS b/chrome/browser/ash/policy/remote_commands/DEPS
index 244bcf53..d0d30da 100644
--- a/chrome/browser/ash/policy/remote_commands/DEPS
+++ b/chrome/browser/ash/policy/remote_commands/DEPS
@@ -38,6 +38,7 @@
   "+chrome/browser/prefs",
   "+chrome/browser/profiles",
   "+chrome/browser/support_tool",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/webui/support_tool",
   "+chrome/common/pref_names.h",
   "+chrome/test/base",
diff --git a/chrome/browser/ash/policy/remote_commands/crd/BUILD.gn b/chrome/browser/ash/policy/remote_commands/crd/BUILD.gn
index 3216489..4c0b278 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/BUILD.gn
+++ b/chrome/browser/ash/policy/remote_commands/crd/BUILD.gn
@@ -92,11 +92,11 @@
     "//chrome/browser",
     "//chrome/browser/ash/app_mode",
     "//chrome/browser/ash/app_mode/web_app",
-    "//chrome/browser/ash/login/ui:test_support",
     "//chrome/browser/ash/policy/remote_commands:test_support",
     "//chrome/browser/ash/policy/remote_commands/crd",
     "//chrome/browser/ash/policy/remote_commands/crd:test_support",
     "//chrome/browser/ash/settings:test_support",
+    "//chrome/browser/ui/ash/login:test_support",
     "//chrome/common:constants",
     "//chrome/test:test_support",
     "//chromeos/ash/services/network_config:in_process_instance",
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc b/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
index 2e75aa4e..5f3c08b0 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
@@ -23,8 +23,8 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/time/time.h"
-#include "chrome/browser/ash/login/ui/mock_login_display_host.h"
 #include "chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.h"
+#include "chrome/browser/ui/ash/login/mock_login_display_host.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.cc b/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.cc
index ec73ecf..b38c7fd 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/remote_activity_notification_controller.cc
@@ -9,7 +9,7 @@
 #include "ash/shell.h"
 #include "base/check_deref.h"
 #include "base/functional/bind.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/policy/status_collector/BUILD.gn b/chrome/browser/ash/policy/status_collector/BUILD.gn
index 170dd2e..c8100a7 100644
--- a/chrome/browser/ash/policy/status_collector/BUILD.gn
+++ b/chrome/browser/ash/policy/status_collector/BUILD.gn
@@ -49,12 +49,12 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/uploading",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/crash_upload_list",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/settings/pages/storage",
     "//chrome/common:channel_info",
     "//chrome/common:chrome_features",
diff --git a/chrome/browser/ash/policy/status_collector/DEPS b/chrome/browser/ash/policy/status_collector/DEPS
index 62765587..a4de5091 100644
--- a/chrome/browser/ash/policy/status_collector/DEPS
+++ b/chrome/browser/ash/policy/status_collector/DEPS
@@ -30,6 +30,7 @@
   "+chrome/browser/crash_upload_list",
   "+chrome/browser/policy",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/webui/ash/settings/pages/storage",
   "+chrome/browser/web_applications/test",
   "+chrome/browser/web_applications/web_app.h",
diff --git a/chrome/browser/ash/policy/status_collector/managed_session_service.cc b/chrome/browser/ash/policy/status_collector/managed_session_service.cc
index 8b47c194..9e31831 100644
--- a/chrome/browser/ash/policy/status_collector/managed_session_service.cc
+++ b/chrome/browser/ash/policy/status_collector/managed_session_service.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/ash/app_mode/kiosk_controller.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/session/user_session_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/user_manager.h"
 
diff --git a/chrome/browser/ash/preferences/BUILD.gn b/chrome/browser/ash/preferences/BUILD.gn
index 025b0c4..d0e68d1 100644
--- a/chrome/browser/ash/preferences/BUILD.gn
+++ b/chrome/browser/ash/preferences/BUILD.gn
@@ -86,10 +86,10 @@
     "//chrome/browser/ash/input_method",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/system",
+    "//chrome/browser/ui/ash/login",
     "//chrome/common:constants",
     "//chromeos/ash/components/settings",
     "//components/prefs",
diff --git a/chrome/browser/ash/preferences/DEPS b/chrome/browser/ash/preferences/DEPS
index 77f7ab7..d072b755 100644
--- a/chrome/browser/ash/preferences/DEPS
+++ b/chrome/browser/ash/preferences/DEPS
@@ -17,6 +17,7 @@
   "+chrome/browser/download",
   "+chrome/browser/prefs",
   # We don't want more dependencies on c/b/ui/ash.
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/system",
   "+chrome/common/chrome_constants.h",
   "+chrome/common/chrome_features.h",
diff --git a/chrome/browser/ash/preferences/preferences_browsertest.cc b/chrome/browser/ash/preferences/preferences_browsertest.cc
index 038563b..9130521e 100644
--- a/chrome/browser/ash/preferences/preferences_browsertest.cc
+++ b/chrome/browser/ash/preferences/preferences_browsertest.cc
@@ -17,11 +17,11 @@
 #include "chrome/browser/ash/input_method/input_method_manager_impl.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/system/fake_input_device_settings.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/profiles/BUILD.gn b/chrome/browser/ash/profiles/BUILD.gn
index 4a02188..fd75d16 100644
--- a/chrome/browser/ash/profiles/BUILD.gn
+++ b/chrome/browser/ash/profiles/BUILD.gn
@@ -52,11 +52,11 @@
   deps += [
     "//chrome/browser/ash/login/enrollment",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
+    "//chrome/browser/ui/ash/login",
   ]
   allow_circular_includes_from = [
     "//chrome/browser/ash/login/enrollment",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/session",
+    "//chrome/browser/ui/ash/login",
   ]
 }
diff --git a/chrome/browser/ash/scalable_iph/BUILD.gn b/chrome/browser/ash/scalable_iph/BUILD.gn
index d8a6a5d..9e1c3e8 100644
--- a/chrome/browser/ash/scalable_iph/BUILD.gn
+++ b/chrome/browser/ash/scalable_iph/BUILD.gn
@@ -132,11 +132,11 @@
     "//chrome/browser/ash/app_list",
     "//chrome/browser/ash/app_list/test:test_support",
     "//chrome/browser/ash/login/lock:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/printing",
     "//chrome/browser/ash/system_web_apps",
     "//chrome/browser/scalable_iph:scalable_iph_factory",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/browser/web_applications",
diff --git a/chrome/browser/ash/scalable_iph/DEPS b/chrome/browser/ash/scalable_iph/DEPS
index 020a5b0..81bd78c 100644
--- a/chrome/browser/ash/scalable_iph/DEPS
+++ b/chrome/browser/ash/scalable_iph/DEPS
@@ -27,6 +27,7 @@
   "+chrome/browser/feature_engagement",
   "+chrome/browser/profiles",
   "+chrome/browser/scalable_iph",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/ash/system_web_apps",
   "+chrome/browser/ui/browser.h",
diff --git a/chrome/browser/ash/scalable_iph/scalable_iph_browsertest.cc b/chrome/browser/ash/scalable_iph/scalable_iph_browsertest.cc
index 171b2d3..d69de1f 100644
--- a/chrome/browser/ash/scalable_iph/scalable_iph_browsertest.cc
+++ b/chrome/browser/ash/scalable_iph/scalable_iph_browsertest.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/ash/app_list/test/chrome_app_list_test_support.h"
 #include "chrome/browser/ash/login/lock/screen_locker_tester.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/printing/cups_print_job.h"
 #include "chrome/browser/ash/printing/cups_print_job_manager.h"
@@ -42,6 +41,7 @@
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/scalable_iph/scalable_iph_factory.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/ash/settings/BUILD.gn b/chrome/browser/ash/settings/BUILD.gn
index 442be1e..b60310b 100644
--- a/chrome/browser/ash/settings/BUILD.gn
+++ b/chrome/browser/ash/settings/BUILD.gn
@@ -46,6 +46,7 @@
     "//base",
     "//chrome/browser:browser_process",
     "//chrome/browser/ash/crosapi:browser_util",
+    "//chrome/browser/ash/tpm",
     "//chrome/browser/profiles:profile",
     "//chrome/common:constants",
     "//chromeos/ash/components/cryptohome",
diff --git a/chrome/browser/ash/settings/DEPS b/chrome/browser/ash/settings/DEPS
index b39ce5d..da5c81c 100644
--- a/chrome/browser/ash/settings/DEPS
+++ b/chrome/browser/ash/settings/DEPS
@@ -23,7 +23,7 @@
   "+chrome/browser/ash/policy/handlers",
   "+chrome/browser/ash/policy/off_hours",
   "+chrome/browser/ash/profiles",
-  "+chrome/browser/ash/tpm_firmware_update.h",
+  "+chrome/browser/ash/tpm",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/net",
   "+chrome/browser/profiles",
diff --git a/chrome/browser/ash/settings/device_settings_provider.cc b/chrome/browser/ash/settings/device_settings_provider.cc
index 72eb44d..375bc4c 100644
--- a/chrome/browser/ash/settings/device_settings_provider.cc
+++ b/chrome/browser/ash/settings/device_settings_provider.cc
@@ -33,7 +33,7 @@
 #include "chrome/browser/ash/settings/device_settings_cache.h"
 #include "chrome/browser/ash/settings/hardware_data_usage_controller.h"
 #include "chrome/browser/ash/settings/stats_reporting_controller.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
diff --git a/chrome/browser/ash/system/DEPS b/chrome/browser/ash/system/DEPS
index 74c78fb..5c05df1e 100644
--- a/chrome/browser/ash/system/DEPS
+++ b/chrome/browser/ash/system/DEPS
@@ -30,6 +30,7 @@
   "+chrome/browser/browser_process_platform_part.h",
   "+chrome/browser/lifetime",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/session",
   "+chrome/browser/ui/webui/ash/login",
   "+chrome/common/chrome_switches.h",
diff --git a/chrome/browser/ash/system/device_disabling_browsertest.cc b/chrome/browser/ash/system/device_disabling_browsertest.cc
index b3b36b1..d2e218e5 100644
--- a/chrome/browser/ash/system/device_disabling_browsertest.cc
+++ b/chrome/browser/ash/system/device_disabling_browsertest.cc
@@ -19,11 +19,11 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/scoped_policy_update.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/system/device_disabling_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/device_disabled_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/network_state_informer.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
diff --git a/chrome/browser/ash/system/device_disabling_manager_default_delegate.cc b/chrome/browser/ash/system/device_disabling_manager_default_delegate.cc
index 5a12e78..6b80849a 100644
--- a/chrome/browser/ash/system/device_disabling_manager_default_delegate.cc
+++ b/chrome/browser/ash/system/device_disabling_manager_default_delegate.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/ash/system/device_disabling_manager_default_delegate.h"
 
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/device_disabled_screen_handler.h"
 
 namespace ash {
diff --git a/chrome/browser/ash/system/timezone_resolver_manager_browsertest.cc b/chrome/browser/ash/system/timezone_resolver_manager_browsertest.cc
index b73478e5..c3956bbb 100644
--- a/chrome/browser/ash/system/timezone_resolver_manager_browsertest.cc
+++ b/chrome/browser/ash/system/timezone_resolver_manager_browsertest.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/policy/test_support/embedded_policy_test_server_mixin.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
@@ -22,6 +21,7 @@
 #include "chrome/browser/browser_process_platform_part_ash.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
diff --git a/chrome/browser/ash/system_web_apps/apps/help_app/BUILD.gn b/chrome/browser/ash/system_web_apps/apps/help_app/BUILD.gn
index be8e00b..ae0675b 100644
--- a/chrome/browser/ash/system_web_apps/apps/help_app/BUILD.gn
+++ b/chrome/browser/ash/system_web_apps/apps/help_app/BUILD.gn
@@ -33,6 +33,7 @@
     "//ash",
     "//ash/constants",
     "//ash/public/cpp",
+    "//ash/webui/help_app_ui:mojo_bindings",
     "//ash/webui/resources:help_app_resources",
     "//ash/webui/settings/public/constants:mojom",
     "//chrome/app:generated_resources",
@@ -152,6 +153,7 @@
   deps = [
     "//ash",
     "//ash/constants",
+    "//ash/webui/help_app_ui:mojo_bindings",
     "//base",
     "//base/test:test_support",
     "//chrome/browser",
diff --git a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc
index 091f2736..bf0da01 100644
--- a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc
+++ b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "ash/constants/ash_features.h"
+#include "ash/webui/help_app_ui/help_app_ui.mojom.h"
 #include "ash/webui/help_app_ui/url_constants.h"
 #include "ash/webui/settings/public/constants/routes.mojom.h"
 #include "base/functional/bind.h"
@@ -226,4 +227,18 @@
   return std::nullopt;
 }
 
+void ChromeHelpAppUIDelegate::OpenSettings(
+    ash::help_app::mojom::SettingsComponent component) {
+  Profile* profile = Profile::FromWebUI(web_ui_);
+
+  switch (component) {
+    case ash::help_app::mojom::SettingsComponent::BLUETOOTH:
+      chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
+          profile, chromeos::settings::mojom::kBluetoothDevicesSubpagePath);
+      return;
+  }
+
+  CHECK(false) << "Invalid settings component value provided";
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.h b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.h
index 1cceb4e..7cbed71 100644
--- a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.h
+++ b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.h
@@ -42,6 +42,7 @@
                          callback) override;
   std::optional<std::string> OpenUrlInBrowserAndTriggerInstallDialog(
       const GURL& url) override;
+  void OpenSettings(help_app::mojom::SettingsComponent component) override;
 
  private:
   raw_ptr<content::WebUI> web_ui_;  // Owns |this|.
diff --git a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate_unittest.cc b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate_unittest.cc
index 0dbfb0f..4000a5e 100644
--- a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate_unittest.cc
@@ -6,16 +6,34 @@
 
 #include <memory>
 
+#include "ash/webui/help_app_ui/help_app_ui.mojom-shared.h"
 #include "base/memory/raw_ptr.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/ash/borealis/testing/features.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "content/public/test/test_web_ui.h"
+#include "testing/gmock/include/gmock/gmock.h"
 
 namespace ash {
 
+namespace {
+
+class MockSetingsWindowManager : public chrome::SettingsWindowManager {
+ public:
+  MOCK_METHOD(void,
+              ShowChromePageForProfile,
+              (Profile * profile,
+               const GURL& gurl,
+               int64_t display_id,
+               apps::LaunchCallback callback),
+              (override));
+};
+
+}  // namespace
+
 class HelpAppUiDelegateTest : public BrowserWithTestWindowTest {
  public:
   HelpAppUiDelegateTest() : web_ui_(std::make_unique<content::TestWebUI>()) {}
@@ -64,4 +82,17 @@
   ASSERT_EQ(device_info_ptr->is_steam_allowed, true);
 }
 
+TEST_F(HelpAppUiDelegateTest, OpenSettingsBluetooth) {
+  MockSetingsWindowManager mock_settings_window_manager;
+  chrome::SettingsWindowManager::SetInstanceForTesting(
+      &mock_settings_window_manager);
+
+  EXPECT_CALL(mock_settings_window_manager,
+              ShowChromePageForProfile(
+                  testing::_, GURL("chrome://os-settings/bluetoothDevices"),
+                  testing::_, testing::_));
+
+  delegate_->OpenSettings(help_app::mojom::SettingsComponent::BLUETOOTH);
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/tpm/BUILD.gn b/chrome/browser/ash/tpm/BUILD.gn
new file mode 100644
index 0000000..3502d5d
--- /dev/null
+++ b/chrome/browser/ash/tpm/BUILD.gn
@@ -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.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash)
+
+static_library("tpm") {
+  sources = [
+    "tpm_firmware_update.cc",
+    "tpm_firmware_update.h",
+    "tpm_firmware_update_notification.cc",
+    "tpm_firmware_update_notification.h",
+  ]
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+
+  deps = [
+    "//ash/constants",
+    "//ash/public/cpp",
+    "//base",
+    "//chrome/browser:browser_process",
+    "//chrome/browser/profiles:profile",
+    "//chrome/common",
+    "//chromeos/ash/components/install_attributes",
+    "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/system",
+    "//components/prefs",
+    "//ui/base",
+    "//ui/chromeos",
+    "//ui/message_center/public/cpp",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [ "tpm_firmware_update_unittest.cc" ]
+
+  deps = [
+    ":tpm",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser/ash/settings:test_support",
+    "//chrome/common",
+    "//chromeos/ash/components/install_attributes:test_support",
+    "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/system",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/ash/tpm/DEPS b/chrome/browser/ash/tpm/DEPS
new file mode 100644
index 0000000..6117a3a
--- /dev/null
+++ b/chrome/browser/ash/tpm/DEPS
@@ -0,0 +1,27 @@
+include_rules = [
+  # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for
+  # details.
+  "-chrome",
+
+  # This directory is in //chrome, which violates the rule above. Allow this
+  # directory to #include its own files.
+  "+chrome/browser/ash/tpm",
+
+  # Existing dependencies within //chrome. There is an active effort to
+  # refactor //chrome/browser/ash to break these dependencies; see b/332804822.
+  # Whenever possible, avoid adding new //chrome dependencies to this list.
+  #
+  # Files residing in certain directories (e.g., //chrome/browser) are listed
+  # individually. Other dependencies within //chrome are listed on a per-
+  # directory basis. See //tools/chromeos/gen_deps.sh for details.
+  "+chrome/browser/ash/policy/core",
+  "+chrome/browser/ash/policy/enrollment",
+  "+chrome/browser/ash/settings",
+  "+chrome/browser/browser_process.h",
+  "+chrome/browser/browser_process_platform_part.h",
+  "+chrome/browser/notifications",
+  "+chrome/browser/profiles/profile.h",
+  "+chrome/browser/ui/settings_window_manager_chromeos.h",
+  "+chrome/common",
+  "+chrome/grit",
+]
diff --git a/chrome/browser/ash/tpm_firmware_update.cc b/chrome/browser/ash/tpm/tpm_firmware_update.cc
similarity index 99%
rename from chrome/browser/ash/tpm_firmware_update.cc
rename to chrome/browser/ash/tpm/tpm_firmware_update.cc
index 665fd11..fb651fb 100644
--- a/chrome/browser/ash/tpm_firmware_update.cc
+++ b/chrome/browser/ash/tpm/tpm_firmware_update.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 
 #include <memory>
 #include <utility>
@@ -38,8 +38,9 @@
 // Decodes a |settings| dictionary into a set of allowed update modes.
 std::set<Mode> GetModesFromSetting(const base::Value* settings) {
   std::set<Mode> modes;
-  if (!settings)
+  if (!settings) {
     return modes;
+  }
 
   const base::Value::Dict& settings_dict = settings->GetDict();
   std::optional<bool> allow_powerwash =
diff --git a/chrome/browser/ash/tpm_firmware_update.h b/chrome/browser/ash/tpm/tpm_firmware_update.h
similarity index 93%
rename from chrome/browser/ash/tpm_firmware_update.h
rename to chrome/browser/ash/tpm/tpm_firmware_update.h
index f2350ea..78536a8 100644
--- a/chrome/browser/ash/tpm_firmware_update.h
+++ b/chrome/browser/ash/tpm/tpm_firmware_update.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 CHROME_BROWSER_ASH_TPM_FIRMWARE_UPDATE_H_
-#define CHROME_BROWSER_ASH_TPM_FIRMWARE_UPDATE_H_
+#ifndef CHROME_BROWSER_ASH_TPM_TPM_FIRMWARE_UPDATE_H_
+#define CHROME_BROWSER_ASH_TPM_TPM_FIRMWARE_UPDATE_H_
 
 #include <memory>
 #include <set>
@@ -66,4 +66,4 @@
 }  // namespace tpm_firmware_update
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_TPM_FIRMWARE_UPDATE_H_
+#endif  // CHROME_BROWSER_ASH_TPM_TPM_FIRMWARE_UPDATE_H_
diff --git a/chrome/browser/ash/tpm_firmware_update_notification.cc b/chrome/browser/ash/tpm/tpm_firmware_update_notification.cc
similarity index 96%
rename from chrome/browser/ash/tpm_firmware_update_notification.cc
rename to chrome/browser/ash/tpm/tpm_firmware_update_notification.cc
index 45dd376..0e2574213 100644
--- a/chrome/browser/ash/tpm_firmware_update_notification.cc
+++ b/chrome/browser/ash/tpm/tpm_firmware_update_notification.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/tpm_firmware_update_notification.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update_notification.h"
 
 #include <string>
 
@@ -12,7 +12,7 @@
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/ash/tpm_firmware_update_notification.h b/chrome/browser/ash/tpm/tpm_firmware_update_notification.h
similarity index 71%
rename from chrome/browser/ash/tpm_firmware_update_notification.h
rename to chrome/browser/ash/tpm/tpm_firmware_update_notification.h
index ea9d51b..1999ccf 100644
--- a/chrome/browser/ash/tpm_firmware_update_notification.h
+++ b/chrome/browser/ash/tpm/tpm_firmware_update_notification.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 CHROME_BROWSER_ASH_TPM_FIRMWARE_UPDATE_NOTIFICATION_H_
-#define CHROME_BROWSER_ASH_TPM_FIRMWARE_UPDATE_NOTIFICATION_H_
+#ifndef CHROME_BROWSER_ASH_TPM_TPM_FIRMWARE_UPDATE_NOTIFICATION_H_
+#define CHROME_BROWSER_ASH_TPM_TPM_FIRMWARE_UPDATE_NOTIFICATION_H_
 
 class Profile;
 
@@ -18,4 +18,4 @@
 }  // namespace tpm_firmware_update
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_TPM_FIRMWARE_UPDATE_NOTIFICATION_H_
+#endif  // CHROME_BROWSER_ASH_TPM_TPM_FIRMWARE_UPDATE_NOTIFICATION_H_
diff --git a/chrome/browser/ash/tpm_firmware_update_unittest.cc b/chrome/browser/ash/tpm/tpm_firmware_update_unittest.cc
similarity index 99%
rename from chrome/browser/ash/tpm_firmware_update_unittest.cc
rename to chrome/browser/ash/tpm/tpm_firmware_update_unittest.cc
index 381d790a..f511075 100644
--- a/chrome/browser/ash/tpm_firmware_update_unittest.cc
+++ b/chrome/browser/ash/tpm/tpm_firmware_update_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 
 #include <utility>
 
diff --git a/chrome/browser/autofill/android/autofill_image_fetcher_impl.cc b/chrome/browser/autofill/android/autofill_image_fetcher_impl.cc
index fde77c5..776a4de 100644
--- a/chrome/browser/autofill/android/autofill_image_fetcher_impl.cc
+++ b/chrome/browser/autofill/android/autofill_image_fetcher_impl.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/autofill/android/autofill_image_fetcher_impl.h"
 
 #include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
 #include "url/android/gurl_android.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
@@ -18,17 +19,23 @@
 AutofillImageFetcherImpl::~AutofillImageFetcherImpl() = default;
 
 void AutofillImageFetcherImpl::FetchImagesForURLs(
-    base::span<const GURL> card_art_urls,
-    base::OnceCallback<void(
-        const std::vector<std::unique_ptr<CreditCardArtImage>>&)> callback) {
-  if (card_art_urls.empty()) {
+    base::span<const GURL> image_urls,
+    base::span<const AutofillImageFetcherBase::ImageSize> image_sizes,
+    base::OnceCallback<
+        void(const std::vector<std::unique_ptr<CreditCardArtImage>>&)>
+        callback_unused) {
+  if (image_urls.empty()) {
     return;
   }
 
   JNIEnv* env = base::android::AttachCurrentThread();
-
-  Java_AutofillImageFetcher_prefetchImages(env, GetOrCreateJavaImageFetcher(),
-                                           card_art_urls);
+  std::vector<int> image_sizes_vector;
+  std::transform(image_sizes.begin(), image_sizes.end(),
+                 std::back_inserter(image_sizes_vector),
+                 [](auto image_size) { return static_cast<int>(image_size); });
+  Java_AutofillImageFetcher_prefetchImages(
+      env, GetOrCreateJavaImageFetcher(), image_urls,
+      base::android::ToJavaIntArray(env, image_sizes_vector));
 }
 
 base::android::ScopedJavaLocalRef<jobject>
diff --git a/chrome/browser/autofill/android/autofill_image_fetcher_impl.h b/chrome/browser/autofill/android/autofill_image_fetcher_impl.h
index fee76ce..0a531dd 100644
--- a/chrome/browser/autofill/android/autofill_image_fetcher_impl.h
+++ b/chrome/browser/autofill/android/autofill_image_fetcher_impl.h
@@ -24,10 +24,11 @@
 
   // AutofillImageFetcherBase:
   void FetchImagesForURLs(
-      base::span<const GURL> card_art_urls,
-      base::OnceCallback<void(
-          const std::vector<std::unique_ptr<CreditCardArtImage>>&)> callback)
-      override;
+      base::span<const GURL> image_urls,
+      base::span<const AutofillImageFetcherBase::ImageSize> image_sizes,
+      base::OnceCallback<
+          void(const std::vector<std::unique_ptr<CreditCardArtImage>>&)>
+          callback_unused) override;
 
   base::android::ScopedJavaLocalRef<jobject> GetOrCreateJavaImageFetcher();
 
diff --git a/chrome/browser/autofill/android/java/res/xml/autofill_options_preferences.xml b/chrome/browser/autofill/android/java/res/xml/autofill_options_preferences.xml
index 61bf4c05..9b2b900f 100644
--- a/chrome/browser/autofill/android/java/res/xml/autofill_options_preferences.xml
+++ b/chrome/browser/autofill/android/java/res/xml/autofill_options_preferences.xml
@@ -18,10 +18,4 @@
         android:key="third_party_toggle_hint"
         android:order="2"
         app:allowDividerBelow="false" />
-
-    <org.chromium.components.browser_ui.settings.TextMessagePreference
-        android:key="third_party_toggle_incognito_note"
-        android:order="3"
-        android:summary="@string/autofill_options_hint_3p_in_incognito"
-        app:allowDividerBelow="false" />
 </PreferenceScreen>
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcher.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcher.java
index c104269..6c22d9f8 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcher.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcher.java
@@ -12,9 +12,9 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.browser.autofill.AutofillUiUtils.CardIconSize;
 import org.chromium.chrome.browser.autofill.AutofillUiUtils.CardIconSpecs;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.embedder_support.simple_factory_key.SimpleFactoryKeyHandle;
 import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.components.image_fetcher.ImageFetcherConfig;
@@ -44,15 +44,15 @@
      * Fetches images for the passed in URLs and stores them in cache.
      *
      * @param urls The URLs to fetch the images.
+     * @param imageSize The list of image sizes that should be fetched for each of the above URLs.
      */
     @CalledByNative
-    void prefetchImages(@JniType("base::span<const GURL>") GURL[] urls) {
+    void prefetchImages(
+            @JniType("base::span<const GURL>") GURL[] urls, @ImageSize int[] imageSizes) {
         Context context = ContextUtils.getApplicationContext();
 
         for (GURL url : urls) {
-            // Credit card art images are shown in 2 different sizes depending on the surface.
-            // Prefetch and cache images in both sizes.
-            for (@CardIconSize int size : new int[] {CardIconSize.SMALL, CardIconSize.LARGE}) {
+            for (@ImageSize int size : imageSizes) {
                 CardIconSpecs cardIconSpecs = CardIconSpecs.create(context, size);
                 fetchImage(url, cardIconSpecs);
             }
@@ -118,7 +118,6 @@
         if (bitmap == null) {
             return;
         }
-
         // When adding new sizes for card icons, check if the corner radius needs to be added as
         // a suffix for caching (crbug.com/1431283).
         mImagesCache.put(
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
index 737a723f..6c6ae42 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillUiUtils.java
@@ -51,6 +51,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.autofill.FieldType;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.payments.LegalMessageLine;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
@@ -101,15 +102,6 @@
         int NONE = 7;
     }
 
-    /** Different sizes in which we show the credit card / bank account art images. */
-    @IntDef({CardIconSize.SMALL, CardIconSize.LARGE, CardIconSize.SQUARE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CardIconSize {
-        int SMALL = 0;
-        int LARGE = 1;
-        int SQUARE = 2;
-    }
-
     /** Contains dimensional specs for credit card icons. */
     public static class CardIconSpecs {
         private final Context mContext;
@@ -142,8 +134,8 @@
          * @param cardIconSize Enum that specifies the icon's size (small or large or square).
          * @return {@link CardIconSpecs} instance containing the specs for the card icon.
          */
-        public static CardIconSpecs create(Context context, @CardIconSize int cardIconSize) {
-            if (cardIconSize == CardIconSize.LARGE
+        public static CardIconSpecs create(Context context, @ImageSize int cardIconSize) {
+            if (cardIconSize == ImageSize.LARGE
                     && ChromeFeatureList.isEnabled(
                             ChromeFeatureList.AUTOFILL_ENABLE_NEW_CARD_ART_AND_NETWORK_IMAGES)) {
                 return new CardIconSpecs(
@@ -153,7 +145,7 @@
                         R.dimen.large_card_icon_corner_radius,
                         R.dimen.card_icon_border_width);
             }
-            if (cardIconSize == CardIconSize.SQUARE) {
+            if (cardIconSize == ImageSize.SQUARE) {
                 return new CardIconSpecs(
                         context,
                         R.dimen.square_card_icon_side_length,
@@ -640,7 +632,7 @@
             PersonalDataManager personalDataManager,
             @Nullable GURL cardArtUrl,
             int defaultIconId,
-            @CardIconSize int cardIconSize,
+            @ImageSize int cardIconSize,
             boolean showCustomIcon) {
         Drawable defaultIcon =
                 defaultIconId == 0 ? null : AppCompatResources.getDrawable(context, defaultIconId);
@@ -747,7 +739,7 @@
             String cardLabel,
             GURL cardArtUrl,
             int defaultIconId,
-            @CardIconSize int cardIconSize,
+            @ImageSize int cardIconSize,
             int iconEndMarginId,
             int cardNameAndNumberTextAppearance,
             int cardLabelTextAppearance,
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
index c616f37..79079d9 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
@@ -22,6 +22,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.autofill.AutofillProfile;
 import org.chromium.components.autofill.IbanRecordType;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.VirtualCardEnrollmentState;
 import org.chromium.components.autofill.payments.BankAccount;
 import org.chromium.components.image_fetcher.ImageFetcher;
@@ -1168,13 +1169,15 @@
         mImageFetcher.prefetchImages(
                 getCreditCardsToSuggest().stream()
                         .map(card -> card.getCardArtUrl())
-                        .toArray(GURL[]::new));
+                        .toArray(GURL[]::new),
+                new int[] {ImageSize.SMALL, ImageSize.LARGE});
     }
 
     /**
      * Return the card art image for the given `customImageUrl`.
-     * @param customImageUrl  URL of the image. If the image is available, it is returned, otherwise
-     *         it is fetched from this URL.
+     *
+     * @param customImageUrl URL of the image. If the image is available, it is returned, otherwise
+     *     it is fetched from this URL.
      * @param cardIconSpecs {@code CardIconSpecs} instance containing the specs for the card icon.
      * @return Bitmap image if found in the local cache, else return an empty object.
      */
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java
index c0ac21d8..89d2886 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java
@@ -54,7 +54,6 @@
 import org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.UserFlow;
 import org.chromium.chrome.browser.autofill.editors.EditorProperties.DropdownKeyValue;
 import org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldItem;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.autofill.AutofillProfile;
 import org.chromium.components.autofill.FieldType;
 import org.chromium.components.autofill.RecordType;
@@ -94,7 +93,6 @@
     private final PropertyModel mCountryField;
     private final PropertyModel mPhoneField;
     private final PropertyModel mEmailField;
-    private final @Nullable PropertyModel mNicknameField;
 
     private List<AutofillAddressUiComponent> mVisibleEditorFields;
     @Nullable private String mCustomDoneButtonText;
@@ -188,18 +186,6 @@
                         .with(VALUE, mProfileToEdit.getInfo(FieldType.EMAIL_ADDRESS))
                         .build();
 
-        // TODO(crbug.com/40267973): Use localized string.
-        mNicknameField =
-                ChromeFeatureList.isEnabled(
-                                ChromeFeatureList
-                                        .AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT)
-                        ? new PropertyModel.Builder(TEXT_ALL_KEYS)
-                                .with(TEXT_FIELD_TYPE, FieldType.UNKNOWN_TYPE)
-                                .with(LABEL, "Label")
-                                .with(IS_REQUIRED, false)
-                                .build()
-                        : null;
-
         assert mCountryField.get(VALUE) != null;
         mPhoneFormatter.setCountryCode(mCountryField.get(VALUE));
     }
@@ -338,7 +324,7 @@
                             || component.id == FieldType.ADDRESS_HOME_DEPENDENT_LOCALITY;
             editorFields.add(new FieldItem(TEXT_INPUT, field, isFullLine));
         }
-        // Phone number (and email/nickname if applicable) are the last fields of the address.
+        // Phone number (and email if applicable) are the last fields of the address.
         if (mPhoneField != null) {
             mPhoneField.set(VALIDATOR, getPhoneValidator(countryCode));
             editorFields.add(new FieldItem(TEXT_INPUT, mPhoneField, /* isFullLine= */ true));
@@ -346,9 +332,6 @@
         if (mEmailField != null) {
             editorFields.add(new FieldItem(TEXT_INPUT, mEmailField, /* isFullLine= */ true));
         }
-        if (mNicknameField != null) {
-            editorFields.add(new FieldItem(TEXT_INPUT, mNicknameField, /* isFullLine= */ true));
-        }
 
         return editorFields;
     }
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsFragment.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsFragment.java
index b3859f72..566cfc68c 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsFragment.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsFragment.java
@@ -28,8 +28,6 @@
     public static final String AUTOFILL_OPTIONS_REFERRER = "autofill-options-referrer";
     public static final String PREF_AUTOFILL_THIRD_PARTY_FILLING = "autofill_third_party_filling";
     public static final String PREF_THIRD_PARTY_TOGGLE_HINT = "third_party_toggle_hint";
-    public static final String PREF_THIRD_PARTY_TOGGLE_INCOGNITO_NOTE =
-            "third_party_toggle_incognito_note";
 
     private @AutofillOptionsReferrer int mReferrer;
 
@@ -73,12 +71,6 @@
         return hint;
     }
 
-    TextMessagePreference getIncognitoNote() {
-        TextMessagePreference note = findPreference(PREF_THIRD_PARTY_TOGGLE_INCOGNITO_NOTE);
-        assert note != null;
-        return note;
-    }
-
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
         mPageTitle.set(getString(R.string.autofill_options_title));
diff --git a/chrome/browser/autofill/android/javatest/src/org/chromium/chrome/browser/autofill/editors/AddressEditorRenderTest.java b/chrome/browser/autofill/android/javatest/src/org/chromium/chrome/browser/autofill/editors/AddressEditorRenderTest.java
index 05f52bd..20e2deb 100644
--- a/chrome/browser/autofill/android/javatest/src/org/chromium/chrome/browser/autofill/editors/AddressEditorRenderTest.java
+++ b/chrome/browser/autofill/android/javatest/src/org/chromium/chrome/browser/autofill/editors/AddressEditorRenderTest.java
@@ -34,7 +34,6 @@
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.autofill.AutofillAddress;
@@ -47,7 +46,6 @@
 import org.chromium.chrome.browser.autofill.PhoneNumberUtilJni;
 import org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.Delegate;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncher;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -74,9 +72,6 @@
 @DoNotBatch(reason = "The tests can't be batched because they run for different set-ups.")
 @RunWith(ParameterizedRunner.class)
 @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
-@EnableFeatures({
-    ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT
-})
 @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
 public class AddressEditorRenderTest extends BlankUiTestActivityTestCase {
     private static final String USER_EMAIL = "example@gmail.com";
@@ -126,7 +121,7 @@
     @Rule
     public final RenderTestRule mRenderTestRule =
             RenderTestRule.Builder.withPublicCorpus()
-                    .setRevision(1)
+                    .setRevision(2)
                     .setBugComponent(Component.UI_BROWSER_AUTOFILL)
                     .build();
 
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java
index 555b8b8..126cf41 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java
@@ -13,7 +13,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyList;
@@ -39,7 +38,6 @@
 import static org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldProperties.IS_REQUIRED;
 import static org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldProperties.LABEL;
 import static org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldProperties.VALUE;
-import static org.chromium.chrome.browser.autofill.editors.EditorProperties.ItemType.DROPDOWN;
 import static org.chromium.chrome.browser.autofill.editors.EditorProperties.ItemType.TEXT_INPUT;
 import static org.chromium.chrome.browser.autofill.editors.EditorProperties.SHOW_REQUIRED_INDICATOR;
 import static org.chromium.chrome.browser.autofill.editors.EditorProperties.TextFieldProperties.TEXT_FIELD_TYPE;
@@ -64,8 +62,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.AutofillAddress;
@@ -79,7 +75,6 @@
 import org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.Delegate;
 import org.chromium.chrome.browser.autofill.editors.EditorProperties.DropdownKeyValue;
 import org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldItem;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.SyncServiceFactory;
@@ -107,7 +102,6 @@
 /** Unit tests for autofill address editor. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@EnableFeatures({ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT})
 public class AddressEditorTest {
     private static final String USER_EMAIL = "example@gmail.com";
     private static final Locale DEFAULT_LOCALE = Locale.getDefault();
@@ -357,8 +351,7 @@
         // editorFields[8] - street address field.
         // editorFields[9] - phone number field.
         // editorFields[10] - email field.
-        // editorFields[11] - nickname field.
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         // Fields obtained from backend must be placed after the country dropdown.
         validateTextField(
@@ -430,7 +423,7 @@
     private void validateErrorMessages(PropertyModel editorModel, boolean errorsPresent) {
         assertNotNull(editorModel);
         ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         Matcher<String> requiredFieldMatcher =
                 errorsPresent ? not(isEmptyString()) : anyOf(nullValue(), isEmptyString());
@@ -450,8 +443,6 @@
                 editorFields.get(9).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
         assertThat(
                 editorFields.get(10).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
-        assertThat(
-                editorFields.get(11).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
     }
 
     @Test
@@ -789,65 +780,6 @@
 
     @Test
     @SmallTest
-    @DisableFeatures({ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT})
-    public void validateDefaultFields_NicknamesDisabled_HonorificDisabled() {
-        setUpAddressUiComponents(new ArrayList());
-        mAddressEditor =
-                new AddressEditorCoordinator(
-                        mActivity,
-                        mDelegate,
-                        mProfile,
-                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
-                        SAVE_NEW_ADDRESS_PROFILE,
-                        /* saveToDisk= */ false);
-        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
-        mAddressEditor.showEditorDialog();
-
-        assertNotNull(mAddressEditor.getEditorModelForTesting());
-        ListModel<FieldItem> editorFields =
-                mAddressEditor.getEditorModelForTesting().get(EDITOR_FIELDS);
-        // Following values are set regardless of the UI components list
-        // received from backend when nicknames are disabled:
-        // editorFields[0] - country dropdown.
-        // editorFields[1] - phone field.
-        // editorFields[2] - email field.
-        assertEquals(3, editorFields.size());
-
-        FieldItem countryDropdownItem = editorFields.get(0);
-        assertEquals(DROPDOWN, countryDropdownItem.type);
-        assertTrue(countryDropdownItem.isFullLine);
-
-        PropertyModel countryDropdown = countryDropdownItem.model;
-        assertEquals(
-                countryDropdown.get(VALUE),
-                AutofillAddress.getCountryCode(sLocalProfile, mPersonalDataManager));
-        assertEquals(
-                countryDropdown.get(LABEL),
-                mActivity.getString(R.string.autofill_profile_editor_country));
-        assertEquals(
-                mSupportedCountries.size(), countryDropdown.get(DROPDOWN_KEY_VALUE_LIST).size());
-        assertThat(
-                mSupportedCountries,
-                containsInAnyOrder(countryDropdown.get(DROPDOWN_KEY_VALUE_LIST).toArray()));
-
-        validateTextField(
-                editorFields.get(1),
-                sLocalProfile.getPhoneNumber(),
-                FieldType.PHONE_HOME_WHOLE_NUMBER,
-                mActivity.getString(R.string.autofill_profile_editor_phone_number),
-                false,
-                true);
-        validateTextField(
-                editorFields.get(2),
-                sLocalProfile.getEmailAddress(),
-                FieldType.EMAIL_ADDRESS,
-                mActivity.getString(R.string.autofill_profile_editor_email_address),
-                false,
-                true);
-    }
-
-    @Test
-    @SmallTest
     public void validateDefaultFields() {
         setUpAddressUiComponents(new ArrayList());
         mAddressEditor =
@@ -869,10 +801,7 @@
         // editorFields[0] - country dropdown.
         // editorFields[1] - phone field.
         // editorFields[2] - email field.
-        // editorFields[3] - nickname field.
-        assertEquals(4, editorFields.size());
-
-        validateTextField(editorFields.get(3), null, FieldType.UNKNOWN_TYPE, "Label", false, true);
+        assertEquals(3, editorFields.size());
     }
 
     @Test
@@ -1052,8 +981,7 @@
         // editorFields[1] - sorting code field.
         // editorFields[2] - phone number field.
         // editorFields[3] - email field.
-        // editorFields[4] - nickname field.
-        assertEquals(5, editorFields.size());
+        assertEquals(4, editorFields.size());
         assertThat(
                 StreamSupport.stream(
                                 Spliterators.spliteratorUnknownSize(
@@ -1065,8 +993,7 @@
                 containsInAnyOrder(
                         FieldType.ADDRESS_HOME_SORTING_CODE,
                         FieldType.PHONE_HOME_WHOLE_NUMBER,
-                        FieldType.EMAIL_ADDRESS,
-                        FieldType.UNKNOWN_TYPE));
+                        FieldType.EMAIL_ADDRESS));
         PropertyModel countryDropdown = editorFields.get(0).model;
 
         setDropdownKey(countryDropdown, "DE");
@@ -1076,8 +1003,7 @@
         // editorFields[1] - street address field.
         // editorFields[2] - phone number field.
         // editorFields[3] - email field.
-        // editorFields[4] - nickname field.
-        assertEquals(5, editorFieldsGermany.size());
+        assertEquals(4, editorFieldsGermany.size());
         assertThat(
                 StreamSupport.stream(
                                 Spliterators.spliteratorUnknownSize(
@@ -1089,8 +1015,7 @@
                 containsInAnyOrder(
                         FieldType.ADDRESS_HOME_STREET_ADDRESS,
                         FieldType.PHONE_HOME_WHOLE_NUMBER,
-                        FieldType.EMAIL_ADDRESS,
-                        FieldType.UNKNOWN_TYPE));
+                        FieldType.EMAIL_ADDRESS));
     }
 
     @Test
@@ -1107,7 +1032,7 @@
         PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
         assertNotNull(editorModel);
         ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         // Set values of the required fields.
         editorFields.get(1).model.set(VALUE, "New Name");
@@ -1145,7 +1070,7 @@
         PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
         assertNotNull(editorModel);
         ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         // Verify behaviour only on the relevant subset of fields.
         editorFields.get(1).model.set(VALUE, "New Name");
@@ -1177,7 +1102,7 @@
         assertNotNull(mAddressEditor.getEditorModelForTesting());
         PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
         ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         // Verify behaviour only on the relevant subset of fields.
         editorFields.get(3).model.set(VALUE, "New locality");
@@ -1223,8 +1148,7 @@
         // editorFields[3] - locality field.
         // editorFields[4] - phone number field.
         // editorFields[5] - email field.
-        // editorFields[6] - nickname field.
-        assertEquals(7, editorFields.size());
+        assertEquals(6, editorFields.size());
 
         editorModel.get(DONE_RUNNABLE).run();
         verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
@@ -1256,7 +1180,7 @@
         PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
         assertNotNull(editorModel);
         ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         PropertyModel countryDropdown = editorFields.get(0).model;
         setDropdownKey(countryDropdown, "CU");
@@ -1298,7 +1222,7 @@
         PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
         assertNotNull(editorModel);
         ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         assertThat(
                 editorFields.get(0).model.get(DROPDOWN_KEY_VALUE_LIST).stream()
@@ -1329,7 +1253,7 @@
         PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
         assertNotNull(editorModel);
         ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         assertThat(
                 editorFields.get(0).model.get(DROPDOWN_KEY_VALUE_LIST).stream()
@@ -1360,7 +1284,7 @@
         PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
         assertNotNull(editorModel);
         ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, editorFields.size());
+        assertEquals(11, editorFields.size());
 
         assertThat(
                 editorFields.get(0).model.get(DROPDOWN_KEY_VALUE_LIST).stream()
@@ -1471,7 +1395,7 @@
         assertNotNull(editorModel);
 
         ListModel<FieldItem> model = editorModel.get(EDITOR_FIELDS);
-        assertEquals(12, model.size());
+        assertEquals(11, model.size());
         for (FieldItem item : model) {
             if (item.model.get(IS_REQUIRED)) {
                 item.model.set(VALUE, "");
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsTest.java
index c703c60..5dcb028 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/options/AutofillOptionsTest.java
@@ -150,7 +150,6 @@
                         .initializeNow();
 
         assertTrue(model.get(THIRD_PARTY_AUTOFILL_ENABLED));
-        assertTrue(mFragment.getIncognitoNote().isShown());
         assertTrue(getRadioButtonComponent().isEnabled());
         assertHintDisplays(getSpannableString(R.string.autofill_options_hint_3p_setting_ready));
     }
diff --git a/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java b/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
index ca6804f..294dc9b 100644
--- a/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
+++ b/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
@@ -41,6 +41,7 @@
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.components.autofill.AutofillProfile;
 import org.chromium.components.autofill.IbanRecordType;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.VerificationStatus;
 import org.chromium.components.autofill.payments.BankAccount;
 import org.chromium.components.autofill.payments.PaymentInstrument;
@@ -251,10 +252,10 @@
     public void testAddCreditCardWithCardArtUrl_imageDownloaded() throws TimeoutException {
         AutofillUiUtils.CardIconSpecs cardIconSpecsLarge =
                 AutofillUiUtils.CardIconSpecs.create(
-                        ContextUtils.getApplicationContext(), AutofillUiUtils.CardIconSize.LARGE);
+                        ContextUtils.getApplicationContext(), ImageSize.LARGE);
         AutofillUiUtils.CardIconSpecs cardIconSpecsSmall =
                 AutofillUiUtils.CardIconSpecs.create(
-                        ContextUtils.getApplicationContext(), AutofillUiUtils.CardIconSize.LARGE);
+                        ContextUtils.getApplicationContext(), ImageSize.LARGE);
         GURL cardArtUrl = new GURL("http://google.com/test.png");
         CreditCard cardWithCardArtUrl =
                 new CreditCard(
@@ -1024,7 +1025,7 @@
             throws TimeoutException {
         Context context = ContextUtils.getApplicationContext();
         AutofillUiUtils.CardIconSpecs cardIconSpecs =
-                AutofillUiUtils.CardIconSpecs.create(context, AutofillUiUtils.CardIconSize.LARGE);
+                AutofillUiUtils.CardIconSpecs.create(context, ImageSize.LARGE);
         GURL cardArtUrl = new GURL("http://google.com/test.png");
         CreditCard cardWithCardArtUrl =
                 new CreditCard(
@@ -1062,8 +1063,7 @@
                                                                     new GURL(
                                                                             "http://google.com/test.png"),
                                                                     R.drawable.mc_card,
-                                                                    AutofillUiUtils.CardIconSize
-                                                                            .LARGE,
+                                                                    ImageSize.LARGE,
                                                                     /* showCustomIcon= */ true))
                                                     .getBitmap()));
                 });
@@ -1109,8 +1109,7 @@
                                                                             .getPersonalDataManagerForLastUsedProfile(),
                                                                     new GURL(""),
                                                                     R.drawable.mc_card,
-                                                                    AutofillUiUtils.CardIconSize
-                                                                            .LARGE,
+                                                                    ImageSize.LARGE,
                                                                     true))
                                                     .getBitmap()));
                 });
@@ -1150,7 +1149,7 @@
                                     AutofillTestHelper.getPersonalDataManagerForLastUsedProfile(),
                                     new GURL(""),
                                     0,
-                                    AutofillUiUtils.CardIconSize.LARGE,
+                                    ImageSize.LARGE,
                                     true));
                 });
     }
@@ -1164,7 +1163,7 @@
         GURL cardArtUrl = new GURL("http://google.com/test.png");
         AutofillUiUtils.CardIconSpecs cardIconSpecs =
                 AutofillUiUtils.CardIconSpecs.create(
-                        ContextUtils.getApplicationContext(), AutofillUiUtils.CardIconSize.LARGE);
+                        ContextUtils.getApplicationContext(), ImageSize.LARGE);
 
         HistogramWatcher expectedHistogram =
                 HistogramWatcher.newSingleRecordWatcher("Autofill.ImageFetcher.Result", true);
@@ -1187,7 +1186,7 @@
         GURL cardArtUrl = new GURL("http://google.com/test.png");
         AutofillUiUtils.CardIconSpecs cardIconSpecs =
                 AutofillUiUtils.CardIconSpecs.create(
-                        ContextUtils.getApplicationContext(), AutofillUiUtils.CardIconSize.LARGE);
+                        ContextUtils.getApplicationContext(), ImageSize.LARGE);
 
         HistogramWatcher expectedHistogram =
                 HistogramWatcher.newSingleRecordWatcher("Autofill.ImageFetcher.Result", false);
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 8309a03..eb62a28 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -726,7 +726,6 @@
     browsing_data::RemoveSiteSettingsData(delete_begin, delete_end,
                                           host_content_settings_map_);
 
-#if !BUILDFLAG(IS_ANDROID)
     // The active permission does not have timestamps, so the all active grants
     // will be revoked regardless of the time range because all the are expected
     // to be recent.
@@ -734,7 +733,6 @@
             FileSystemAccessPermissionContextFactory::GetForProfile(profile_)) {
       permission_context->RevokeAllActiveGrants();
     }
-#endif
 
     auto* handler_registry =
         ProtocolHandlerRegistryFactory::GetForBrowserContext(profile_);
@@ -864,16 +862,16 @@
         ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW, delete_begin_,
         delete_end_, website_settings_filter);
 
+    host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
+        ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY, delete_begin,
+        delete_end, website_settings_filter);
+
 #if !BUILDFLAG(IS_ANDROID)
     host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
         ContentSettingsType::INTENT_PICKER_DISPLAY, delete_begin_, delete_end_,
         website_settings_filter);
 
     host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
-        ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY, delete_begin,
-        delete_end, website_settings_filter);
-
-    host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
         ContentSettingsType::PRIVATE_NETWORK_GUARD, delete_begin_, delete_end_,
         website_settings_filter);
     host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
diff --git a/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.cc b/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.cc
index 1544008..f4a8c6d 100644
--- a/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.cc
+++ b/chrome/browser/certificate_provider/security_token_pin_dialog_host_popup_impl.cc
@@ -21,7 +21,7 @@
 #include "ui/views/window/dialog_delegate.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #endif
 
 namespace chromeos {
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 7001a6c0..5e9a862 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -169,7 +169,8 @@
 #if !defined(OFFICIAL_BUILD)
 #include "chrome/browser/ui/webui/new_tab_page/foo/foo.mojom.h"  // nogncheck crbug.com/1125897
 #endif
-#include "chrome/browser/ui/lens/lens_untrusted_ui.h"
+#include "chrome/browser/ui/lens/lens_overlay_untrusted_ui.h"
+#include "chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h"
 #include "chrome/browser/ui/lens/search_bubble_ui.h"
 #include "chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h"
 #include "chrome/browser/ui/webui/commerce/product_specifications_ui.h"
@@ -1206,8 +1207,11 @@
       NewTabPageThirdPartyUI>(map);
 
   if (lens::features::IsLensOverlayEnabled()) {
+    RegisterWebUIControllerInterfaceBinder<
+        lens::mojom::LensSidePanelPageHandlerFactory,
+        lens::LensSidePanelUntrustedUI>(map);
     RegisterWebUIControllerInterfaceBinder<lens::mojom::LensPageHandlerFactory,
-                                           lens::LensUntrustedUI>(map);
+                                           lens::LensOverlayUntrustedUI>(map);
   }
 
   if (lens::features::IsLensOverlayEnabled() &&
@@ -1962,11 +1966,16 @@
 #endif  // BUILDFLAG(ENABLE_COMPOSE)
 #if !BUILDFLAG(IS_ANDROID)
   if (lens::features::IsLensOverlayEnabled()) {
-    registry.ForWebUI<lens::LensUntrustedUI>()
-        .Add<lens::mojom::LensPageHandlerFactory>()
+    registry.ForWebUI<lens::LensSidePanelUntrustedUI>()
+        .Add<lens::mojom::LensSidePanelPageHandlerFactory>()
         .Add<searchbox::mojom::PageHandler>()
         .Add<color_change_listener::mojom::PageHandler>();
   }
+  if (lens::features::IsLensOverlayEnabled()) {
+    registry.ForWebUI<lens::LensOverlayUntrustedUI>()
+        .Add<lens::mojom::LensPageHandlerFactory>()
+        .Add<color_change_listener::mojom::PageHandler>();
+  }
   if (lens::features::IsLensOverlaySearchBubbleEnabled()) {
     registry.ForWebUI<lens::SearchBubbleUI>()
         .Add<lens::mojom::SearchBubblePageHandlerFactory>()
diff --git a/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc b/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc
index 697d943..9fbc4ae 100644
--- a/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc
+++ b/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc
@@ -1759,39 +1759,47 @@
 
   for (auto test : cases) {
     SCOPED_TRACE(test.name);
-    std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder =
-        std::make_unique<ukm::TestAutoSetUkmRecorder>();
 
-    // Check that a same-origin access does not register use counters.
-    EXPECT_TRUE(content::ExecJs(same_origin_subframe, test.property));
-    CheckCounter(test.property_access, 0);
-    CheckCounter(test.property_access_from_other_page, 0);
+    // Check that same-origin access does not register use counters.
+    {
+      std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder =
+          std::make_unique<ukm::TestAutoSetUkmRecorder>();
+      EXPECT_TRUE(content::ExecJs(same_origin_subframe, test.property));
+      CheckCounter(test.property_access, 0);
+      CheckCounter(test.property_access_from_other_page, 0);
+      const auto& entries =
+          test_ukm_recorder->GetEntriesByName("WindowProxyUsage");
+      ASSERT_EQ(entries.size(), 0u);
+    }
 
-    // Check that a cross-origin access register use counters.
-    EXPECT_TRUE(content::ExecJs(cross_origin_subframe, test.property));
-    CheckCounter(test.property_access, 1);
-    CheckCounter(test.property_access_from_other_page, 0);
-    const auto& entries =
-        test_ukm_recorder->GetEntriesByName("WindowProxyUsage");
-    ASSERT_EQ(entries.size(), 1u);
-    const auto& entry = entries.back();
-    test_ukm_recorder->ExpectEntryMetric(entry, "AccessType",
-                                         (int)test.access_type);
-    test_ukm_recorder->ExpectEntryMetric(entry, "IsSamePage", 1);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalFrameContext",
-                                         2 /*SubFrameCrossSite*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalPageContext",
-                                         0 /*Window*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalUserActivationState",
-                                         0 /*IsActive*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemoteFrameContext",
-                                         0 /*TopFrame*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemotePageContext",
-                                         0 /*Window*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemoteUserActivationState",
-                                         0 /*IsActive*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "StorageKeyComparison",
-                                         1 /*SameTopSiteCrossOrigin*/);
+    // Check that cross-origin access does register use counters.
+    {
+      std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder =
+          std::make_unique<ukm::TestAutoSetUkmRecorder>();
+      EXPECT_TRUE(content::ExecJs(cross_origin_subframe, test.property));
+      CheckCounter(test.property_access, 1);
+      CheckCounter(test.property_access_from_other_page, 0);
+      auto entries = test_ukm_recorder->GetEntriesByName("WindowProxyUsage");
+      ASSERT_EQ(entries.size(), 1u);
+      auto entry = entries.back();
+      test_ukm_recorder->ExpectEntryMetric(entry, "AccessType",
+                                           (int)test.access_type);
+      test_ukm_recorder->ExpectEntryMetric(entry, "IsSamePage", 1);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalFrameContext",
+                                           2 /*SubFrameCrossSite*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalPageContext",
+                                           0 /*Window*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalUserActivationState",
+                                           0 /*IsActive*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemoteFrameContext",
+                                           0 /*TopFrame*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemotePageContext",
+                                           0 /*Window*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemoteUserActivationState",
+                                           0 /*IsActive*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "StorageKeyComparison",
+                                           1 /*SameTopSiteCrossOrigin*/);
+    }
   }
 }
 
@@ -2042,39 +2050,47 @@
 
   for (auto test : cases) {
     SCOPED_TRACE(test.name);
-    std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder =
-        std::make_unique<ukm::TestAutoSetUkmRecorder>();
 
-    // Check that a same-origin access does not register use counters.
-    EXPECT_TRUE(content::ExecJs(same_origin_popup, test.property));
-    CheckCounter(test.property_access, 0);
-    CheckCounter(test.property_access_from_other_page, 0);
+    // Check that same-origin access does not register use counters.
+    {
+      std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder =
+          std::make_unique<ukm::TestAutoSetUkmRecorder>();
+      EXPECT_TRUE(content::ExecJs(same_origin_popup, test.property));
+      CheckCounter(test.property_access, 0);
+      CheckCounter(test.property_access_from_other_page, 0);
+      const auto& entries =
+          test_ukm_recorder->GetEntriesByName("WindowProxyUsage");
+      ASSERT_EQ(entries.size(), 0u);
+    }
 
-    // Check that a cross-origin access register use counters.
-    EXPECT_TRUE(content::ExecJs(cross_origin_popup, test.property));
-    CheckCounter(test.property_access, 1);
-    CheckCounter(test.property_access_from_other_page, 1);
-    const auto& entries =
-        test_ukm_recorder->GetEntriesByName("WindowProxyUsage");
-    ASSERT_EQ(entries.size(), 1u);
-    const auto& entry = entries.back();
-    test_ukm_recorder->ExpectEntryMetric(entry, "AccessType",
-                                         (int)test.access_type);
-    test_ukm_recorder->ExpectEntryMetric(entry, "IsSamePage", 0);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalFrameContext",
-                                         0 /*TopFrame*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalPageContext",
-                                         1 /*Popup*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalUserActivationState",
-                                         0 /*IsActive*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemoteFrameContext",
-                                         0 /*TopFrame*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemotePageContext",
-                                         0 /*Window*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemoteUserActivationState",
-                                         1 /*HasBeenActive*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "StorageKeyComparison",
-                                         3 /*CrossKey*/);
+    // Check that cross-origin access does register use counters.
+    {
+      std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder =
+          std::make_unique<ukm::TestAutoSetUkmRecorder>();
+      EXPECT_TRUE(content::ExecJs(cross_origin_popup, test.property));
+      CheckCounter(test.property_access, 1);
+      CheckCounter(test.property_access_from_other_page, 1);
+      auto entries = test_ukm_recorder->GetEntriesByName("WindowProxyUsage");
+      ASSERT_EQ(entries.size(), 1u);
+      auto entry = entries.back();
+      test_ukm_recorder->ExpectEntryMetric(entry, "AccessType",
+                                           (int)test.access_type);
+      test_ukm_recorder->ExpectEntryMetric(entry, "IsSamePage", 0);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalFrameContext",
+                                           0 /*TopFrame*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalPageContext",
+                                           1 /*Popup*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalUserActivationState",
+                                           0 /*IsActive*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemoteFrameContext",
+                                           0 /*TopFrame*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemotePageContext",
+                                           0 /*Window*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemoteUserActivationState",
+                                           1 /*HasBeenActive*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "StorageKeyComparison",
+                                           3 /*CrossKey*/);
+    }
   }
 }
 
@@ -2092,83 +2108,83 @@
   } cases[] = {
       {
           "blur",
-          "window.opener.blur()",
+          "try { window.opener.blur(); } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessBlur,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageBlur,
           blink::mojom::WindowProxyAccessType::kBlur,
       },
       {
           "closed",
-          "window.opener.closed",
+          "try { window.opener.closed; } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessClosed,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageClosed,
           blink::mojom::WindowProxyAccessType::kClosed,
       },
       {
           "focus",
-          "window.opener.focus()",
+          "try { window.opener.focus(); } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessFocus,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageFocus,
           blink::mojom::WindowProxyAccessType::kFocus,
       },
       {
           "frames",
-          "window.opener.frames",
+          "try { window.opener.frames; } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessFrames,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageFrames,
           blink::mojom::WindowProxyAccessType::kFrames,
       },
       {
           "length",
-          "window.opener.length",
+          "try { window.opener.length; } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessLength,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLength,
           blink::mojom::WindowProxyAccessType::kLength,
       },
       {
           "location get",
-          "window.opener.location",
+          "try { window.opener.location; } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessLocation,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageLocation,
           blink::mojom::WindowProxyAccessType::kLocation,
       },
       {
           "opener get",
-          "window.opener.opener",
+          "try { window.opener.opener; } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessOpener,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageOpener,
           blink::mojom::WindowProxyAccessType::kOpener,
       },
       {
           "parent",
-          "window.opener.parent",
+          "try { window.opener.parent; } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessParent,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageParent,
           blink::mojom::WindowProxyAccessType::kParent,
       },
       {
           "postMessage",
-          "window.opener.postMessage('','*')",
+          "try { window.opener.postMessage('','*'); } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessPostMessage,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPagePostMessage,
           blink::mojom::WindowProxyAccessType::kPostMessage,
       },
       {
           "self",
-          "window.opener.self",
+          "try { window.opener.self; } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessSelf,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageSelf,
           blink::mojom::WindowProxyAccessType::kSelf,
       },
       {
           "top",
-          "window.opener.top",
+          "try { window.opener.top; } catch (_) {}",
           WebFeature::kWindowProxyCrossOriginAccessTop,
           WebFeature::kWindowProxyCrossOriginAccessFromOtherPageTop,
           blink::mojom::WindowProxyAccessType::kTop,
       }};
 
-  // Check that a same-origin access does not register use counters.
+  // Check that same-origin access does not register use counters.
   content::WebContents* same_origin_popin = OpenPopup(url, /*is_popin=*/true);
   for (auto test : cases) {
     SCOPED_TRACE(test.name);
@@ -2182,39 +2198,45 @@
     ASSERT_EQ(entries.size(), 0u);
   }
 
-  // Check that a cross-origin access register use counters.
+  // Check that cross-origin access does register use counters.
   BrowserWindow::FindBrowserWindowWithWebContents(same_origin_popin)->Close();
   GURL cross_origin_url = https_server().GetURL("b.test", "/empty.html");
   content::WebContents* cross_origin_popin =
       OpenPopup(cross_origin_url, /*is_popin=*/true);
   for (auto test : cases) {
     SCOPED_TRACE(test.name);
+    bool is_closed =
+        test.access_type == blink::mojom::WindowProxyAccessType::kClosed;
+    bool is_post_message =
+        test.access_type == blink::mojom::WindowProxyAccessType::kPostMessage;
     std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder =
         std::make_unique<ukm::TestAutoSetUkmRecorder>();
     EXPECT_TRUE(content::ExecJs(cross_origin_popin, test.property));
-    CheckCounter(test.property_access, 1);
-    CheckCounter(test.property_access_from_other_page, 1);
-    const auto& entries =
-        test_ukm_recorder->GetEntriesByName("WindowProxyUsage");
-    ASSERT_EQ(entries.size(), 1u);
-    const auto& entry = entries.back();
-    test_ukm_recorder->ExpectEntryMetric(entry, "AccessType",
-                                         (int)test.access_type);
-    test_ukm_recorder->ExpectEntryMetric(entry, "IsSamePage", 0);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalFrameContext",
-                                         0 /*TopFrame*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalPageContext",
-                                         2 /*PartitionedPopin*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "LocalUserActivationState",
-                                         0 /*IsActive*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemoteFrameContext",
-                                         0 /*TopFrame*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemotePageContext",
-                                         0 /*Window*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "RemoteUserActivationState",
-                                         1 /*HasBeenActive*/);
-    test_ukm_recorder->ExpectEntryMetric(entry, "StorageKeyComparison",
-                                         1 /*SameTopSiteCrossOrigin*/);
+    CheckCounter(test.property_access, is_closed || is_post_message ? 1 : 0);
+    CheckCounter(test.property_access_from_other_page,
+                 is_closed || is_post_message ? 1 : 0);
+    auto entries = test_ukm_recorder->GetEntriesByName("WindowProxyUsage");
+    ASSERT_EQ(entries.size(), is_post_message || is_closed ? 1u : 0u);
+    if (is_closed || is_post_message) {
+      auto entry = entries.back();
+      test_ukm_recorder->ExpectEntryMetric(entry, "AccessType",
+                                           (int)test.access_type);
+      test_ukm_recorder->ExpectEntryMetric(entry, "IsSamePage", 0);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalFrameContext",
+                                           0 /*TopFrame*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalPageContext",
+                                           2 /*PartitionedPopin*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "LocalUserActivationState",
+                                           0 /*IsActive*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemoteFrameContext",
+                                           0 /*TopFrame*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemotePageContext",
+                                           0 /*Window*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "RemoteUserActivationState",
+                                           1 /*HasBeenActive*/);
+      test_ukm_recorder->ExpectEntryMetric(entry, "StorageKeyComparison",
+                                           1 /*SameTopSiteCrossOrigin*/);
+    }
   }
 }
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 39dc75f..cbfcac3f 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -405,7 +405,6 @@
       "//ash/constants",
       "//chrome/browser/ash/crosapi",
       "//chrome/browser/ash/crosapi:test_support",
-      "//chrome/browser/ash/login/ui:test_support",
       "//chrome/browser/ash/login/users:test_support",
       "//chrome/browser/ash/printing:test_support",
       "//chrome/browser/ash/printing/history",
@@ -413,6 +412,7 @@
       "//chrome/browser/ash/printing/print_management",
       "//chrome/browser/ash/system_web_apps/test_support:test_support",
       "//chrome/browser/extensions:test_support",
+      "//chrome/browser/ui/ash/login:test_support",
       "//chrome/browser/ui/ash/system_web_apps:system_web_apps",
       "//chrome/browser/web_applications:web_applications_test_support",
       "//chromeos/ash/components/dbus/session_manager",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/BUILD.gn b/chrome/browser/chromeos/extensions/login_screen/login/BUILD.gn
index 858078b..e8a737d 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/login_screen/login/BUILD.gn
@@ -116,12 +116,12 @@
       "//base",
       "//chrome/browser:browser_process",
       "//chrome/browser/ash/login",
-      "//chrome/browser/ash/login/ui:test_support",
       "//chrome/browser/ash/login/users:test_support",
       "//chrome/browser/chromeos/extensions/login_screen/login/cleanup",
       "//chrome/browser/chromeos/extensions/login_screen/login/cleanup:test_support",
       "//chrome/browser/chromeos/extensions/login_screen/login/external_logout_done",
       "//chrome/browser/chromeos/extensions/login_screen/login/external_logout_request",
+      "//chrome/browser/ui/ash/login:test_support",
       "//chrome/common:constants",
       "//chrome/test:test_support",
       "//chromeos/ash/components/login/auth/public:authpublic",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/DEPS b/chrome/browser/chromeos/extensions/login_screen/login/DEPS
index 4e714d8..d179ff0a 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/DEPS
+++ b/chrome/browser/chromeos/extensions/login_screen/login/DEPS
@@ -16,7 +16,6 @@
   "+chrome/browser/ash/login/lock",
   "+chrome/browser/ash/login/signin_specifics.h",
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/policy/test_support",
   "+chrome/browser/ash/profiles",
@@ -25,6 +24,7 @@
   "+chrome/browser/extensions/extension_api_unittest.h",
   "+chrome/browser/lifetime/termination_notification.h",
   "+chrome/browser/policy/extension_force_install_mixin.h",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/session",
   "+chrome/common",
   "+chrome/test",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api_ash_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api_ash_unittest.cc
index 3126f55..5212484 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api_ash_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api_ash_unittest.cc
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/memory/raw_ptr.h"
-#include "chrome/browser/chromeos/extensions/login_screen/login/login_api.h"
-
 #include <map>
 #include <memory>
 #include <string>
@@ -13,13 +10,13 @@
 #include "ash/constants/ash_pref_names.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/signin_specifics.h"
-#include "chrome/browser/ash/login/ui/mock_login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.h"
@@ -27,9 +24,11 @@
 #include "chrome/browser/chromeos/extensions/login_screen/login/errors.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/external_logout_done/external_logout_done_event_handler.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/external_logout_request/external_logout_request_event_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/login_api.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.h"
 #include "chrome/browser/extensions/extension_api_unittest.h"
+#include "chrome/browser/ui/ash/login/mock_login_display_host.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/commerce/android/BUILD.gn b/chrome/browser/commerce/android/BUILD.gn
index 8fd67b8..9d04a7b6 100644
--- a/chrome/browser/commerce/android/BUILD.gn
+++ b/chrome/browser/commerce/android/BUILD.gn
@@ -11,7 +11,6 @@
   sources = [
     "java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentBinder.java",
     "java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentCoordinator.java",
-    "java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentListener.java",
     "java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediator.java",
     "java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentProperties.java",
     "java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentProvider.java",
diff --git a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentCoordinator.java b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentCoordinator.java
index 669cfae..9068151 100644
--- a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentCoordinator.java
+++ b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentCoordinator.java
@@ -6,6 +6,8 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -15,16 +17,20 @@
 import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
 import androidx.recyclerview.widget.RecyclerView.State;
 
+import org.chromium.base.CallbackController;
 import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.List;
 
 /** Coordinator for building a commerce bottom sheet content. */
 public class CommerceBottomSheetContentCoordinator {
+    private static final long CONTENT_PROVIDER_TIMEOUT_MS = 200;
+
     /** Supported content types, the content is prioritized based on this order. */
     @IntDef({ContentType.PRICE_TRACKING, ContentType.DISCOUNTS, ContentType.PRICE_INSIGHTS})
     @Retention(RetentionPolicy.SOURCE)
@@ -34,13 +40,15 @@
         int PRICE_INSIGHTS = 2;
     }
 
-    private List<CommerceBottomSheetContentProvider> mContentProviders;
+    private List<CommerceBottomSheetContentProvider> mContentProviders = new ArrayList<>();
     private final CommerceBottomSheetContentMediator mMediator;
     private RecyclerView mContenRecyclerView;
     private View mCommerceBottomSheetContentContainer;
     private ModelList mModelList;
 
     private final Context mContext;
+    private CallbackController mCallbackController;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     public CommerceBottomSheetContentCoordinator(Context context) {
         mContext = context;
@@ -76,14 +84,22 @@
                     }
                 });
 
-        mMediator = new CommerceBottomSheetContentMediator(mModelList);
+        mMediator = new CommerceBottomSheetContentMediator(mModelList, mContentProviders.size());
     }
 
     /** Request to show the bottom sheet. */
     public void requestShowContent() {
+        mCallbackController = new CallbackController();
         for (CommerceBottomSheetContentProvider provider : mContentProviders) {
-            provider.requestShowContent(mMediator);
+            provider.requestContent(mCallbackController.makeCancelable(mMediator::onContentReady));
         }
+
+        mHandler.postDelayed(
+                () -> {
+                    mCallbackController.destroy();
+                    mMediator.timeOut();
+                },
+                CONTENT_PROVIDER_TIMEOUT_MS);
     }
 
     public RecyclerView getRecyclerViewForTesting() {
diff --git a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentListener.java b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentListener.java
deleted file mode 100644
index a32286c..0000000
--- a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentListener.java
+++ /dev/null
@@ -1,20 +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.
-
-package org.chromium.chrome.browser.commerce;
-
-import org.chromium.ui.modelutil.PropertyModel;
-
-/** A listener for individual feature content. */
-public interface CommerceBottomSheetContentListener {
-
-    /**
-     * Called when the content is ready to show.
-     *
-     * @param model The PropertyModel contains all relevant content for the CommerceBottomSheet. The
-     *     model should contain the following PropertyKey: TYPE, HAS_TITLE, TITLE_TEXT, and
-     *     CONTENT_VIEW.
-     */
-    void onContentReady(PropertyModel model);
-}
diff --git a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediator.java b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediator.java
index ea4a9565..96c0e690 100644
--- a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediator.java
+++ b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediator.java
@@ -4,21 +4,32 @@
 
 package org.chromium.chrome.browser.commerce;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.Arrays;
 
-public class CommerceBottomSheetContentMediator implements CommerceBottomSheetContentListener {
+public class CommerceBottomSheetContentMediator {
     private final ModelList mModelList;
+    private int mContentReadyCount;
+    private final int mExpectedContentCount;
 
-    public CommerceBottomSheetContentMediator(ModelList modelList) {
+    public CommerceBottomSheetContentMediator(ModelList modelList, int expectedContentCount) {
         mModelList = modelList;
+        mExpectedContentCount = expectedContentCount;
     }
 
-    @Override
-    public void onContentReady(PropertyModel model) {
+    public void onContentReady(@Nullable PropertyModel model) {
+        mContentReadyCount++;
+
+        if (model == null) {
+            requestToShowBottomSheetIfReady();
+            return;
+        }
+
         assert isValidPropertyModel(model)
                 : "Miss required property in PropertyModel from"
                         + " CommerceBottomSheetContentProperties.";
@@ -35,10 +46,25 @@
         }
 
         mModelList.add(index, new ListItem(0, model));
+        requestToShowBottomSheetIfReady();
+    }
+
+    public void timeOut() {
+        showBottomSheet();
     }
 
     private boolean isValidPropertyModel(PropertyModel model) {
         return model.getAllProperties()
                 .containsAll(Arrays.asList(CommerceBottomSheetContentProperties.ALL_KEYS));
     }
+
+    private void requestToShowBottomSheetIfReady() {
+        if (mContentReadyCount < mExpectedContentCount) return;
+        showBottomSheet();
+    }
+
+    private void showBottomSheet() {
+        // TODO(b/362359865): create BottomSheetContent and request to show with the
+        // BottomSheetController.
+    }
 }
diff --git a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentProvider.java b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentProvider.java
index fd42caa..8120cb4 100644
--- a/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentProvider.java
+++ b/chrome/browser/commerce/android/java/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentProvider.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.commerce;
 
+import org.chromium.base.Callback;
+import org.chromium.ui.modelutil.PropertyModel;
+
 /**
  * The interface to be implemented by the individual feature to show a View in the
  * CommerceBottomSheet.
@@ -13,10 +16,12 @@
     /**
      * Request the content to show in the CommerceBottomSheetContent.
      *
-     * @param listener The listener that will run after the feature is finished gathering all
-     *     content.
+     * @param contentReadyCallback The callback that will run after the feature is finished
+     *     gathering all content. PropertyModel can be null if there's nothing to show. Otherwise,
+     *     the PropertyModel should contain the following PropertyKey: TYPE, HAS_TITLE, TITLE_TEXT,
+     *     and CONTENT_VIEW.
      */
-    void requestShowContent(CommerceBottomSheetContentListener listener);
+    void requestContent(Callback<PropertyModel> contentReadyCallback);
 
     /** Called when the BottomSheet hides. */
     void hideContentView();
diff --git a/chrome/browser/commerce/android/javatests/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentRenderTest.java b/chrome/browser/commerce/android/javatests/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentRenderTest.java
index 2548e7f..a605a8f 100644
--- a/chrome/browser/commerce/android/javatests/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentRenderTest.java
+++ b/chrome/browser/commerce/android/javatests/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentRenderTest.java
@@ -42,7 +42,6 @@
     private ModelList mModelList;
     private View mContentView;
     private RecyclerView mRecyclerView;
-
     private CommerceBottomSheetContentCoordinator mCoordinator;
 
     private PropertyModel createPropertyModel(int type, boolean hasTitle) {
diff --git a/chrome/browser/commerce/android/junit/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediatorUnitTest.java b/chrome/browser/commerce/android/junit/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediatorUnitTest.java
index 0c776bfd..39c02d4 100644
--- a/chrome/browser/commerce/android/junit/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediatorUnitTest.java
+++ b/chrome/browser/commerce/android/junit/src/org/chromium/chrome/browser/commerce/CommerceBottomSheetContentMediatorUnitTest.java
@@ -28,7 +28,10 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mModelList = new ModelList();
-        mMediator = new CommerceBottomSheetContentMediator(mModelList);
+    }
+
+    private void setupMediator(int expectedContentCount) {
+        mMediator = new CommerceBottomSheetContentMediator(mModelList, expectedContentCount);
     }
 
     private PropertyModel createPropertyModel(int type) {
@@ -42,11 +45,13 @@
 
     @Test(expected = AssertionError.class)
     public void testOnContentReady_assertOnInvalidPropertyModel() {
+        setupMediator(1);
         mMediator.onContentReady(new PropertyModel());
     }
 
     @Test
     public void testOnContentReady_firstPropertyModel() {
+        setupMediator(1);
         mMediator.onContentReady(createPropertyModel(0));
 
         assertEquals(1, mModelList.size());
@@ -54,6 +59,7 @@
 
     @Test
     public void testOnContentReady_MultiPropertyModels() {
+        setupMediator(3);
         PropertyModel model0 = createPropertyModel(0);
         PropertyModel model1 = createPropertyModel(1);
         PropertyModel model2 = createPropertyModel(2);
@@ -70,6 +76,7 @@
 
     @Test(expected = AssertionError.class)
     public void testOnContentReady_assertOnSameType() {
+        setupMediator(1);
         PropertyModel model0 = createPropertyModel(0);
         PropertyModel model1 = createPropertyModel(0);
 
diff --git a/chrome/browser/commerce/price_insights/android/java/res/layout/price_insights_container.xml b/chrome/browser/commerce/price_insights/android/java/res/layout/price_insights_container.xml
index 8e3f42ae8..49ea5f2 100644
--- a/chrome/browser/commerce/price_insights/android/java/res/layout/price_insights_container.xml
+++ b/chrome/browser/commerce/price_insights/android/java/res/layout/price_insights_container.xml
@@ -30,7 +30,9 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical">
+            android:orientation="vertical"
+            android:clickable="true"
+            android:focusable="true">
 
             <!-- Price tracking section -->
             <include layout="@layout/price_tracking_layout"/>
diff --git a/chrome/browser/controlled_frame/controlled_frame_disabled_permission_browsertest.cc b/chrome/browser/controlled_frame/controlled_frame_disabled_permission_browsertest.cc
index 5eb3f40..5248ba4 100644
--- a/chrome/browser/controlled_frame/controlled_frame_disabled_permission_browsertest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_disabled_permission_browsertest.cc
@@ -5,18 +5,23 @@
 #include <string>
 
 #include "chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.h"
+#include "chrome/browser/serial/serial_chooser_context.h"
+#include "chrome/browser/serial/serial_chooser_context_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/usb/usb_browser_test_utils.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "services/device/public/cpp/test/fake_serial_port_manager.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
+#include "services/device/public/mojom/serial.mojom.h"
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-forward.h"
 
 namespace {
 constexpr char kFakeUsbDeviceSerialNumber[] = "123456";
-constexpr char kUsbError[] = "error";
+constexpr char kFailResult[] = "error";
+constexpr char kExpectedPortsLength[] = "1";
 }  // namespace
 
 namespace controlled_frame {
@@ -74,11 +79,11 @@
   }
 })();
   )",
-                                                kUsbError);
+                                                kFailResult);
   test_case.policy_features.insert(
       blink::mojom::PermissionsPolicyFeature::kUsb);
   test_case.success_result = kFakeUsbDeviceSerialNumber;
-  test_case.failure_result = kUsbError;
+  test_case.failure_result = kFailResult;
 
   DisabledPermissionTestParam test_param = GetParam();
   VerifyDisabledPermission(test_case, test_param);
@@ -94,4 +99,77 @@
                            return info.param.name;
                          });
 
+class ControlledFrameDisabledPermissionSerialTest
+    : public ControlledFrameDisabledPermissionTest {
+ public:
+  void SetUpOnMainThread() override {
+    ControlledFrameDisabledPermissionTest::SetUpOnMainThread();
+    mojo::PendingRemote<device::mojom::SerialPortManager> port_manager;
+    port_manager_.AddReceiver(port_manager.InitWithNewPipeAndPassReceiver());
+    context()->SetPortManagerForTesting(std::move(port_manager));
+  }
+
+  void TearDownOnMainThread() override {
+    ControlledFrameDisabledPermissionTest::TearDownOnMainThread();
+  }
+
+  device::FakeSerialPortManager& port_manager() { return port_manager_; }
+  SerialChooserContext* context() {
+    return SerialChooserContextFactory::GetForProfile(browser()->profile());
+  }
+
+  void CreatePortAndGrantPermissionToOrigin(const url::Origin& origin) {
+    // Create port and grant permission to it.
+    auto port = device::mojom::SerialPortInfo::New();
+    port->token = base::UnguessableToken::Create();
+    context()->GrantPortPermission(origin, *port);
+    port_manager().AddPort(std::move(port));
+  }
+
+ private:
+  device::FakeSerialPortManager port_manager_;
+};
+
+IN_PROC_BROWSER_TEST_P(ControlledFrameDisabledPermissionSerialTest, WebSerial) {
+  DisabledPermissionTestCase test_case;
+  test_case.request_script = content::JsReplace(R"(
+(async () => {
+  try {
+    const ports =
+      await navigator.serial.getPorts().then(ports => ports.length);
+    return ports !== 0 ? ports.toString() : $1;
+  } catch (_) {
+    return $1;
+  }
+})()
+  )",
+                                                kFailResult);
+
+  test_case.policy_features.insert(
+      blink::mojom::PermissionsPolicyFeature::kSerial);
+  test_case.success_result = kExpectedPortsLength;
+  test_case.failure_result = kFailResult;
+
+  DisabledPermissionTestParam test_param = GetParam();
+  auto [app_frame, controlled_frame] =
+      SetUpControlledFrame(test_case, test_param);
+  if (!app_frame || !controlled_frame) {
+    return;
+  }
+  CreatePortAndGrantPermissionToOrigin(app_frame->GetLastCommittedOrigin());
+  CreatePortAndGrantPermissionToOrigin(
+      controlled_frame->GetLastCommittedOrigin());
+  VerifyDisabledPermission(test_case, test_param, app_frame, controlled_frame);
+}
+
+INSTANTIATE_TEST_SUITE_P(/*no prefix*/
+                         ,
+                         ControlledFrameDisabledPermissionSerialTest,
+                         testing::ValuesIn(
+                             GetDefaultDisabledPermissionTestParams()),
+                         [](const testing::TestParamInfo<
+                             DisabledPermissionTestParam>& info) {
+                           return info.param.name;
+                         });
+
 }  // namespace controlled_frame
diff --git a/chrome/browser/controlled_frame/controlled_frame_interactive_browsertest.cc b/chrome/browser/controlled_frame/controlled_frame_interactive_browsertest.cc
index 52ec47f..56eff9de 100644
--- a/chrome/browser/controlled_frame/controlled_frame_interactive_browsertest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_interactive_browsertest.cc
@@ -2,11 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <set>
+
 #include "build/build_config.h"
 #include "chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-forward.h"
 
 namespace controlled_frame {
 
@@ -59,6 +62,8 @@
     })();
   )";
   test_case.permission_name = "fullscreen";
+  test_case.policy_features.insert(
+      {blink::mojom::PermissionsPolicyFeature::kFullscreen});
 
   PermissionRequestTestParam test_param = GetParam();
   VerifyEnabledPermission(test_case, test_param);
diff --git a/chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.cc b/chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.cc
index 109afb6..15dd4ed 100644
--- a/chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.cc
@@ -290,26 +290,19 @@
 void ControlledFramePermissionRequestTestBase::VerifyDisabledPermission(
     const DisabledPermissionTestCase& test_case,
     const DisabledPermissionTestParam& test_param) {
-  // If the permission has no dependent permissions policy feature, then skip
-  // the true negative permissions policy test cases.
-  if (!test_param.policy_features_enabled &&
-      test_case.policy_features.empty()) {
+  auto [app_frame, controlled_frame] =
+      SetUpControlledFrame(test_case, test_param);
+  if (!app_frame || !controlled_frame) {
     return;
   }
+  VerifyDisabledPermission(test_case, test_param, app_frame, controlled_frame);
+}
 
-  web_app::ManifestBuilder manifest_builder = web_app::ManifestBuilder();
-  if (test_param.policy_features_enabled) {
-    for (auto& policy_feature : test_case.policy_features) {
-      manifest_builder.AddPermissionsPolicyWildcard(policy_feature);
-    }
-  }
-
-  auto [app_frame, controlled_frame] =
-      InstallAndOpenIwaThenCreateControlledFrame(
-          /*controlled_frame_host_name=*/kPermissionAllowedHost,
-          /*controlled_frame_src_relative_url=*/"/index.html",
-          manifest_builder);
-
+void ControlledFramePermissionRequestTestBase::VerifyDisabledPermission(
+    const DisabledPermissionTestCase& test_case,
+    const DisabledPermissionTestParam& test_param,
+    content::RenderFrameHost* app_frame,
+    content::RenderFrameHost* controlled_frame) {
   std::string expected_iwa_result = test_param.iwa_expect_success
                                         ? test_case.success_result
                                         : test_case.failure_result;
@@ -344,4 +337,28 @@
             false);
 }
 
+std::pair<content::RenderFrameHost*, content::RenderFrameHost*>
+ControlledFramePermissionRequestTestBase::SetUpControlledFrame(
+    const DisabledPermissionTestCase& test_case,
+    const DisabledPermissionTestParam& test_param) {
+  // If the permission has no dependent permissions policy feature, then
+  // skip
+  // the true negative permissions policy test cases.
+  if (!test_param.policy_features_enabled &&
+      test_case.policy_features.empty()) {
+    return {nullptr, nullptr};
+  }
+
+  web_app::ManifestBuilder manifest_builder = web_app::ManifestBuilder();
+  if (test_param.policy_features_enabled) {
+    for (auto& policy_feature : test_case.policy_features) {
+      manifest_builder.AddPermissionsPolicyWildcard(policy_feature);
+    }
+  }
+
+  return InstallAndOpenIwaThenCreateControlledFrame(
+      /*controlled_frame_host_name=*/kPermissionAllowedHost,
+      /*controlled_frame_src_relative_url=*/"/index.html", manifest_builder);
+}
+
 }  // namespace controlled_frame
diff --git a/chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.h b/chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.h
index d9c376b5..aa38e396 100644
--- a/chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.h
+++ b/chrome/browser/controlled_frame/controlled_frame_permission_request_test_base.h
@@ -99,6 +99,15 @@
   void VerifyDisabledPermission(const DisabledPermissionTestCase& test_case,
                                 const DisabledPermissionTestParam& test_param);
 
+  void VerifyDisabledPermission(const DisabledPermissionTestCase& test_case,
+                                const DisabledPermissionTestParam& test_param,
+                                content::RenderFrameHost* app_frame,
+                                content::RenderFrameHost* controlled_frame);
+
+  std::pair<content::RenderFrameHost*, content::RenderFrameHost*>
+  SetUpControlledFrame(const DisabledPermissionTestCase& test_case,
+                       const DisabledPermissionTestParam& test_param);
+
  private:
   void SetUpPermissionRequestEventListener(
       content::RenderFrameHost* app_frame,
diff --git a/chrome/browser/controlled_frame/controlled_frame_test_base.cc b/chrome/browser/controlled_frame/controlled_frame_test_base.cc
index 682dfab7..1dcc9b4a4 100644
--- a/chrome/browser/controlled_frame/controlled_frame_test_base.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_test_base.cc
@@ -131,12 +131,12 @@
 ControlledFrameTestBase::InstallAndOpenIwaThenCreateControlledFrame(
     std::optional<std::string_view> controlled_frame_host_name,
     std::string_view controlled_frame_src_relative_url,
-    web_app::ManifestBuilder manfest_buider) {
+    web_app::ManifestBuilder manfest_builder) {
   CHECK(embedded_https_test_server().Started())
       << "Controlled Frame content server has not been started.";
 
   web_app::IsolatedWebAppUrlInfo url_info =
-      CreateAndInstallEmptyApp(manfest_buider);
+      CreateAndInstallEmptyApp(manfest_builder);
   content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
   CHECK(app_frame);
 
diff --git a/chrome/browser/controlled_frame/controlled_frame_test_base.h b/chrome/browser/controlled_frame/controlled_frame_test_base.h
index 1d55c59..61b2c294 100644
--- a/chrome/browser/controlled_frame/controlled_frame_test_base.h
+++ b/chrome/browser/controlled_frame/controlled_frame_test_base.h
@@ -76,7 +76,7 @@
   InstallAndOpenIwaThenCreateControlledFrame(
       std::optional<std::string_view> controlled_frame_host_name,
       std::string_view controlled_frame_src_relative_url,
-      web_app::ManifestBuilder manfest_buider = web_app::ManifestBuilder());
+      web_app::ManifestBuilder manfest_builder = web_app::ManifestBuilder());
 
   extensions::WebViewGuest* GetWebViewGuest(
       content::RenderFrameHost* embedder_frame);
diff --git a/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles.xml b/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles.xml
index b4eacc11..43801ed 100644
--- a/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles.xml
+++ b/chrome/browser/data_sharing/android/java/res/layout/shared_image_tiles.xml
@@ -38,17 +38,6 @@
             android:gravity="center"
             android:visibility="gone"/>
 
-        <org.chromium.ui.widget.ChromeImageButton
-            android:src="@drawable/ic_person_add_40dp"
-            android:id="@+id/shared_image_tiles_add"
-            android:layout_height="@dimen/shared_image_tiles_icon_height"
-            android:layout_width="@dimen/shared_image_tiles_add_width"
-            android:layout_gravity="center"
-            android:scaleType="centerInside"
-            android:background="@android:color/transparent"
-            android:tint="@color/default_icon_color_secondary_tint_list"
-            android:visibility="gone" />
-
     </LinearLayout>
 
 </org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesView>
diff --git a/chrome/browser/data_sharing/android/java/res/values/dimens.xml b/chrome/browser/data_sharing/android/java/res/values/dimens.xml
index 7844bb1..ab4fcf5 100644
--- a/chrome/browser/data_sharing/android/java/res/values/dimens.xml
+++ b/chrome/browser/data_sharing/android/java/res/values/dimens.xml
@@ -9,7 +9,6 @@
     <!-- Dimens for shared_image_tiles view -->
     <dimen name="shared_image_tiles_icon_height">28dp</dimen>
     <dimen name="shared_image_tiles_icon_radius">14dp</dimen>
-    <dimen name="shared_image_tiles_add_width">24dp</dimen>
     <dimen name="shared_image_tiles_overlap_margin">8dp</dimen>
     <dimen name="shared_image_tiles_overlap_margin_negative">-6dp</dimen>
 
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
index 7066c321..6c63648b 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
@@ -15,7 +15,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
-import org.chromium.base.Token;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
@@ -24,11 +23,8 @@
 import org.chromium.chrome.browser.share.ChromeShareExtras;
 import org.chromium.chrome.browser.share.ChromeShareExtras.DetailedContentType;
 import org.chromium.chrome.browser.share.ShareDelegate;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupUiActionHandler;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
@@ -52,8 +48,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This class is responsible for handling communication from the UI to multiple data sharing
@@ -68,26 +65,20 @@
     private final ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
     private final WindowAndroid mWindowAndroid;
     private final Resources mResources;
-    private final OneshotSupplier<TabGroupModelFilter> mTabGroupModelFilterSupplier;
     private final OneshotSupplier<TabGroupUiActionHandler> mTabGroupUiActionHandlerSupplier;
     private Callback<Profile> mProfileObserver;
-    private List<SyncObserver> mSyncObserversList;
-    private List<TabGroupModelObserver> mTabGroupModelObserversList;
+    private Map</*collaborationId*/ String, SyncObserver> mSyncObserversList;
 
     /** This class is responsible for observing sync tab activities. */
     private static class SyncObserver implements TabGroupSyncService.Observer {
-        interface OnTabGroupAddedCallback {
-            void onResult(SyncObserver observer, SavedTabGroup group);
-        }
-
         private final String mDataSharingGroupId;
         private final TabGroupSyncService mTabGroupSyncService;
-        private OnTabGroupAddedCallback mCallback;
+        private Callback<SavedTabGroup> mCallback;
 
         SyncObserver(
                 String dataSharingGroupId,
                 TabGroupSyncService tabGroupSyncService,
-                OnTabGroupAddedCallback callback) {
+                Callback<SavedTabGroup> callback) {
             mDataSharingGroupId = dataSharingGroupId;
             mTabGroupSyncService = tabGroupSyncService;
             mCallback = callback;
@@ -98,9 +89,9 @@
         @Override
         public void onTabGroupAdded(SavedTabGroup group, @TriggerSource int source) {
             if (mDataSharingGroupId.equals(group.collaborationId)) {
-                OnTabGroupAddedCallback callback = mCallback;
+                Callback<SavedTabGroup> callback = mCallback;
                 destroy();
-                callback.onResult(this, group);
+                callback.onResult(group);
             }
         }
 
@@ -110,50 +101,6 @@
         }
     }
 
-    /** This class is responsible for waiting for the next tab group to be created. */
-    private static class TabGroupModelObserver implements TabGroupModelFilterObserver {
-        interface OnCreateNewGroupCallback {
-            void onResult(TabGroupModelObserver observer, int tabId);
-        }
-
-        private final String mSyncId;
-        private final TabGroupModelFilter mTabGroupModelFilter;
-        private final TabGroupSyncService mTabGroupSyncService;
-        private OnCreateNewGroupCallback mCallback;
-
-        TabGroupModelObserver(
-                String syncId,
-                TabGroupModelFilter tabGroupModelFilter,
-                TabGroupSyncService tabGroupSyncService,
-                OnCreateNewGroupCallback callback) {
-            mSyncId = syncId;
-            mTabGroupModelFilter = tabGroupModelFilter;
-            mTabGroupSyncService = tabGroupSyncService;
-            mCallback = callback;
-
-            mTabGroupModelFilter.addTabGroupObserver(this);
-        }
-
-        @Override
-        public void didCreateNewGroup(Tab destinationTab, TabGroupModelFilter filter) {
-            Token groupId = destinationTab.getTabGroupId();
-            assert groupId != null;
-            SavedTabGroup savedTabGroup = mTabGroupSyncService.getGroup(mSyncId);
-            assert savedTabGroup.localId != null;
-            if (savedTabGroup.localId.equals(new LocalTabGroupId(groupId))) {
-                int tabId = destinationTab.getId();
-                OnCreateNewGroupCallback callback = mCallback;
-                destroy();
-                callback.onResult(this, tabId);
-            }
-        }
-
-        void destroy() {
-            mTabGroupModelFilter.removeTabGroupObserver(this);
-            mCallback = null;
-        }
-    }
-
     /**
      * Constructor for a new {@link DataSharingTabManager} object.
      *
@@ -163,8 +110,6 @@
      * @param shareDelegateSupplier The supplier of share delegate.
      * @param windowAndroid The window base class that has the minimum functionality.
      * @param resources Used to load localized android resources.
-     * @param tabGroupModelFilterSupplier The {@link TabGroupModelFilter} for the current non
-     *     incognito tab group.
      * @param tabGroupUiActionHandlerSupplier Supplier for the controller used to open tab groups
      *     locally.
      */
@@ -175,18 +120,15 @@
             ObservableSupplier<ShareDelegate> shareDelegateSupplier,
             WindowAndroid windowAndroid,
             Resources resources,
-            OneshotSupplier<TabGroupModelFilter> tabGroupModelFilterSupplier,
             OneshotSupplier<TabGroupUiActionHandler> tabGroupUiActionHandlerSupplier) {
         mDataSharingTabSwitcherDelegate = tabSwitcherDelegate;
-        mTabGroupModelFilterSupplier = tabGroupModelFilterSupplier;
         mProfileSupplier = profileSupplier;
         mBottomSheetControllerSupplier = bottomSheetControllerSupplier;
         mShareDelegateSupplier = shareDelegateSupplier;
         mWindowAndroid = windowAndroid;
         mResources = resources;
         mTabGroupUiActionHandlerSupplier = tabGroupUiActionHandlerSupplier;
-        mSyncObserversList = new ArrayList<SyncObserver>();
-        mTabGroupModelObserversList = new ArrayList<TabGroupModelObserver>();
+        mSyncObserversList = new HashMap<>();
         assert mProfileSupplier != null;
         assert mBottomSheetControllerSupplier != null;
         assert mShareDelegateSupplier != null;
@@ -194,13 +136,10 @@
 
     /** Cleans up any outstanding resources. */
     public void destroy() {
-        for (SyncObserver observer : mSyncObserversList) {
-            observer.destroy();
+        for (Map.Entry<String, SyncObserver> entry : mSyncObserversList.entrySet()) {
+            entry.getValue().destroy();
         }
-
-        for (TabGroupModelObserver observer : mTabGroupModelObserversList) {
-            observer.destroy();
-        }
+        mSyncObserversList.clear();
     }
 
     // These values are persisted to logs. Entries should not be renumbered and numeric values
@@ -284,9 +223,9 @@
         }
 
         GroupToken groupToken = parseResult.groupToken;
+        String groupId = groupToken.groupId;
         // Verify that tab group does not already exist in sync.
-        SavedTabGroup existingGroup =
-                getTabGroupForCollabIdFromSync(groupToken.groupId, tabGroupSyncService);
+        SavedTabGroup existingGroup = getTabGroupForCollabIdFromSync(groupId, tabGroupSyncService);
         if (existingGroup != null) {
             RecordHistogram.recordEnumeratedHistogram(
                     "DataSharing.Android.JoinActionFlowState",
@@ -297,17 +236,18 @@
         }
 
         // TODO(b/354003616): Show loading dialog while waiting for tab.
+        if (!mSyncObserversList.containsKey(groupId)) {
+            SyncObserver syncObserver =
+                    new SyncObserver(
+                            groupId,
+                            tabGroupSyncService,
+                            (group) -> {
+                                onSavedTabGroupAvailable(group);
+                                mSyncObserversList.remove(group.collaborationId);
+                            });
 
-        SyncObserver syncObserver =
-                new SyncObserver(
-                        groupToken.groupId,
-                        tabGroupSyncService,
-                        (observer, group) -> {
-                            onSavedTabGroupAvailable(group);
-                            mSyncObserversList.remove(observer);
-                        });
-
-        mSyncObserversList.add(syncObserver);
+            mSyncObserversList.put(groupId, syncObserver);
+        }
 
         dataSharingService.addMember(
                 groupToken.groupId,
@@ -358,7 +298,9 @@
      *
      * @param tabId The tab id of the first tab in the group.
      */
-    void switchToTabGroupWithTabId(int tabId) {
+    void switchToTabGroup(SavedTabGroup group) {
+        Integer tabId = group.savedTabs.get(0).localId;
+        assert tabId != null;
         // TODO(b/354003616): Verify that the loading dialog is gone.
         mDataSharingTabSwitcherDelegate.openTabGroupWithTabId(tabId);
     }
@@ -377,9 +319,7 @@
                     "DataSharing.Android.JoinActionFlowState",
                     JoinActionStateAndroid.LOCAL_TAB_GROUP_EXISITS,
                     JoinActionStateAndroid.COUNT);
-            Integer tabId = group.savedTabs.get(0).localId;
-            assert tabId != null;
-            switchToTabGroupWithTabId(tabId);
+            switchToTabGroup(group);
             return;
         }
 
@@ -387,37 +327,24 @@
     }
 
     void openLocalTabGroup(SavedTabGroup group) {
-        // Note: This does not switch the active tab to the opened tab.
-        Profile originalProfile = mProfileSupplier.get().getOriginalProfile();
-        TabGroupModelFilter tabGroupModelFilter = mTabGroupModelFilterSupplier.get();
-        TabGroupModelObserver tabGroupModelObserver =
-                new TabGroupModelObserver(
-                        group.syncId,
-                        tabGroupModelFilter,
-                        TabGroupSyncServiceFactory.getForProfile(originalProfile),
-                        (observer, tabId) -> {
-                            RecordHistogram.recordEnumeratedHistogram(
-                                    "DataSharing.Android.JoinActionFlowState",
-                                    JoinActionStateAndroid.LOCAL_TAB_GROUP_ADDED,
-                                    JoinActionStateAndroid.COUNT);
-                            switchToTabGroupWithTabId(tabId);
-                            RecordHistogram.recordEnumeratedHistogram(
-                                    "DataSharing.Android.JoinActionFlowState",
-                                    JoinActionStateAndroid.LOCAL_TAB_GROUP_OPENED,
-                                    JoinActionStateAndroid.COUNT);
-                            mTabGroupModelObserversList.remove(observer);
-                        });
+        TabGroupSyncService tabGroupSyncService =
+                TabGroupSyncServiceFactory.getForProfile(
+                        mProfileSupplier.get().getOriginalProfile());
 
-        mTabGroupModelObserversList.add(tabGroupModelObserver);
-
-        String syncId = group.syncId;
-        if (mTabGroupUiActionHandlerSupplier.hasValue()) {
-            mTabGroupUiActionHandlerSupplier.get().openTabGroup(syncId);
-            return;
-        }
-        mTabGroupUiActionHandlerSupplier.onAvailable(
+        mTabGroupUiActionHandlerSupplier.runSyncOrOnAvailable(
                 (tabGroupUiActionHandler) -> {
-                    tabGroupUiActionHandler.openTabGroup(syncId);
+                    // Note: This does not switch the active tab to the opened tab.
+                    tabGroupUiActionHandler.openTabGroup(group.syncId);
+                    RecordHistogram.recordEnumeratedHistogram(
+                            "DataSharing.Android.JoinActionFlowState",
+                            JoinActionStateAndroid.LOCAL_TAB_GROUP_ADDED,
+                            JoinActionStateAndroid.COUNT);
+                    SavedTabGroup savedTabGroup = tabGroupSyncService.getGroup(group.syncId);
+                    switchToTabGroup(savedTabGroup);
+                    RecordHistogram.recordEnumeratedHistogram(
+                            "DataSharing.Android.JoinActionFlowState",
+                            JoinActionStateAndroid.LOCAL_TAB_GROUP_OPENED,
+                            JoinActionStateAndroid.COUNT);
                 });
     }
 
@@ -466,15 +393,6 @@
     // LINT.ThenChange(//tools/metrics/histograms/data_sharing/enums.xml:ShareActionStateAndroid)
 
     /**
-     * Stop observing a data sharing tab group from local tab model.
-     *
-     * @param observer The observer to be removed.
-     */
-    protected void deleteLocalTabModelObserver(TabGroupModelObserver observer) {
-        mTabGroupModelFilterSupplier.get().removeTabGroupObserver(observer);
-    }
-
-    /**
      * Creates a collaboration group.
      *
      * @param activity The activity in which the group is to be created.
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java
index 4485bfa..68007b4 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java
@@ -9,6 +9,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -87,6 +88,7 @@
             new ActivityScenarioRule<>(TestActivity.class);
 
     private static final String GROUP_ID = "group_id";
+    private static final String SYNC_ID = "sync_id";
     private static final Integer ROOT_ID = 123;
     private static final LocalTabGroupId LOCAL_ID = new LocalTabGroupId(Token.createRandom());
     private static final Integer TAB_ID = 456;
@@ -122,8 +124,6 @@
             new OneshotSupplierImpl<>();
     private ObservableSupplier<Profile> mProfileSupplier;
     private Supplier<BottomSheetController> mBottomSheetControllerSupplier;
-    private OneshotSupplierImpl<TabGroupModelFilter> mTabGroupModelFilterSupplier =
-            new OneshotSupplierImpl<>();
     private ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
 
     @Before
@@ -137,7 +137,6 @@
                 new ObservableSupplierImpl<BottomSheetController>(mBottomSheetController);
         mShareDelegateSupplier = new ObservableSupplierImpl<ShareDelegate>(mShareDelegate);
         mTabGroupUiActionHandlerSupplier.set(mTabGroupUiActionHandler);
-        mTabGroupModelFilterSupplier.set(mTabGroupModelFilter);
 
         mDataSharingTabManager =
                 new DataSharingTabManager(
@@ -147,11 +146,11 @@
                         mShareDelegateSupplier,
                         mWindowAndroid,
                         ApplicationProvider.getApplicationContext().getResources(),
-                        mTabGroupModelFilterSupplier,
                         mTabGroupUiActionHandlerSupplier);
 
         mSavedTabGroup = new SavedTabGroup();
         mSavedTabGroup.collaborationId = GROUP_ID;
+        mSavedTabGroup.syncId = SYNC_ID;
         mSavedTabGroup.localId = LOCAL_ID;
         SavedTabGroupTab savedTabGroupTab = new SavedTabGroupTab();
         savedTabGroupTab.localId = TAB_ID;
@@ -199,7 +198,6 @@
                         mShareDelegateSupplier,
                         mWindowAndroid,
                         ApplicationProvider.getApplicationContext().getResources(),
-                        mTabGroupModelFilterSupplier,
                         mTabGroupUiActionHandlerSupplier);
         mDataSharingTabManager.initiateJoinFlow(TEST_URL);
 
@@ -250,13 +248,19 @@
         doReturn(new String[] {GROUP_ID}).when(mTabGroupSyncService).getAllGroupIds();
 
         // Mock does not exist in local tab model.
-        mSavedTabGroup.localId = null;
-        doReturn(mSavedTabGroup).when(mTabGroupSyncService).getGroup(GROUP_ID);
+        SavedTabGroup savedTabGroupWithoutLocalId = new SavedTabGroup();
+        savedTabGroupWithoutLocalId.collaborationId = GROUP_ID;
+        savedTabGroupWithoutLocalId.syncId = SYNC_ID;
+        SavedTabGroupTab savedTabGroupTab = new SavedTabGroupTab();
+        savedTabGroupWithoutLocalId.savedTabs.add(savedTabGroupTab);
+
+        when(mTabGroupSyncService.getGroup(GROUP_ID)).thenReturn(savedTabGroupWithoutLocalId);
+        when(mTabGroupSyncService.getGroup(SYNC_ID)).thenReturn(mSavedTabGroup);
 
         mDataSharingTabManager.initiateJoinFlow(TEST_URL);
 
-        verify(mTabGroupUiActionHandler).openTabGroup(any());
-        verify(mTabGroupModelFilter).addTabGroupObserver(any());
+        verify(mTabGroupUiActionHandler).openTabGroup(SYNC_ID);
+        verify(mDataSharingTabSwitcherDelegate).openTabGroupWithTabId(TAB_ID);
     }
 
     @Test
@@ -266,8 +270,12 @@
         doReturn(new String[0]).when(mTabGroupSyncService).getAllGroupIds();
 
         mDataSharingTabManager.initiateJoinFlow(TEST_URL);
+
+        // The same group should not be observed twice.
+        mDataSharingTabManager.initiateJoinFlow(TEST_URL);
+
         verify(mTabGroupSyncService).addObserver(any());
-        verify(mDataSharingService).addMember(eq(GROUP_ID), eq(ACCESS_TOKEN), any());
+        verify(mDataSharingService, times(2)).addMember(eq(GROUP_ID), eq(ACCESS_TOKEN), any());
     }
 
     @Test
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinator.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinator.java
index 496fc80..431328c 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinator.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinator.java
@@ -105,15 +105,13 @@
     }
 
     private void extractGroupMemberInfo(GroupData groupData) {
-        // TODO(b/361642045): Call showAvatars here.
-        updateTilesCount(groupData.members.size());
-
         List<String> emails = new ArrayList<>();
         for (GroupMember member : groupData.members) {
             if (member.email != null && !member.email.isEmpty()) {
                 emails.add(member.email);
             }
         }
+        updateTilesCount(emails.size());
 
         // Let the UI delegate draw the icon tiles.
         DataSharingUIDelegate dataSharingUIDelegate = mDataSharingService.getUIDelegate();
@@ -156,11 +154,6 @@
             mModel.set(
                     SharedImageTilesProperties.REMAINING_TILES,
                     mAvailableTileCount - maxTilesToShowWithNumberTile);
-        } else {
-            if (mType == SharedImageTilesType.CLICKABLE && mIconTilesCount < MAX_TILES_UI_LIMIT) {
-                // Append an add person button.
-                mModel.set(SharedImageTilesProperties.SHOW_ADD_BUTTON, true);
-            }
         }
     }
 
@@ -190,7 +183,6 @@
         // TODO(b/325533985): |mAvailableTileCount| should be replace by the actual number of icons
         // needed.
         mAvailableTileCount = count;
-        mModel.set(SharedImageTilesProperties.SHOW_ADD_BUTTON, false);
         mModel.set(SharedImageTilesProperties.REMAINING_TILES, 0);
         mModel.set(SharedImageTilesProperties.ICON_TILES, 0);
         initializeSharedImageTiles();
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinatorUnitTest.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinatorUnitTest.java
index 4bd124f9..25eb5929 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinatorUnitTest.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesCoordinatorUnitTest.java
@@ -34,7 +34,6 @@
 import org.chromium.components.data_sharing.GroupData;
 import org.chromium.components.data_sharing.GroupMember;
 import org.chromium.components.data_sharing.PeopleGroupActionFailure;
-import org.chromium.ui.widget.ChromeImageButton;
 
 import java.util.Arrays;
 
@@ -52,7 +51,6 @@
     private Context mContext;
     private SharedImageTilesCoordinator mSharedImageTilesCoordinator;
     private SharedImageTilesView mView;
-    private ChromeImageButton mButtonTileView;
     private TextView mCountTileView;
 
     @Before
@@ -65,12 +63,10 @@
         mSharedImageTilesCoordinator =
                 new SharedImageTilesCoordinator(mContext, type, color, mDataSharingService);
         mView = mSharedImageTilesCoordinator.getView();
-        mButtonTileView = mView.findViewById(R.id.shared_image_tiles_add);
         mCountTileView = mView.findViewById(R.id.tiles_count);
     }
 
-    private void verifyViews(int buttonVisibility, int countVisibility, int iconViewCount) {
-        assertEquals(mButtonTileView.getVisibility(), buttonVisibility);
+    private void verifyViews(int countVisibility, int iconViewCount) {
         assertEquals(mCountTileView.getVisibility(), countVisibility);
         assertEquals(mSharedImageTilesCoordinator.getAllIconViews().size(), iconViewCount);
     }
@@ -89,43 +85,19 @@
         // 3 tile count: Tile Tile Tile
         // 4 tile count: Tile Tile +2
         // etc
-        verifyViews(View.GONE, View.GONE, /* iconViewCount= */ 0);
+        verifyViews(View.GONE, /* iconViewCount= */ 0);
 
         mSharedImageTilesCoordinator.updateTilesCount(1);
-        verifyViews(View.GONE, View.GONE, /* iconViewCount= */ 1);
+        verifyViews(View.GONE, /* iconViewCount= */ 1);
 
         mSharedImageTilesCoordinator.updateTilesCount(2);
-        verifyViews(View.GONE, View.GONE, /* iconViewCount= */ 2);
+        verifyViews(View.GONE, /* iconViewCount= */ 2);
 
         mSharedImageTilesCoordinator.updateTilesCount(3);
-        verifyViews(View.GONE, View.GONE, /* iconViewCount= */ 3);
+        verifyViews(View.GONE, /* iconViewCount= */ 3);
 
         mSharedImageTilesCoordinator.updateTilesCount(4);
-        verifyViews(View.GONE, View.VISIBLE, /* iconViewCount= */ 2);
-    }
-
-    @Test
-    public void testClickableTheme() {
-        initialize(SharedImageTilesType.CLICKABLE, SharedImageTilesColor.DEFAULT);
-        // Clickable theme should have the following view logic:
-        // 1 tile: Tile (add user)
-        // 2 tiles Tile Tile (add user)
-        // 3 tiles: Tile Tile Tile
-        // 4 tiles: Tile Tile +2
-        // etc
-        verifyViews(View.GONE, View.GONE, /* iconViewCount= */ 0);
-
-        mSharedImageTilesCoordinator.updateTilesCount(1);
-        verifyViews(View.VISIBLE, View.GONE, /* iconViewCount= */ 1);
-
-        mSharedImageTilesCoordinator.updateTilesCount(2);
-        verifyViews(View.VISIBLE, View.GONE, /* iconViewCount= */ 2);
-
-        mSharedImageTilesCoordinator.updateTilesCount(3);
-        verifyViews(View.GONE, View.GONE, /* iconViewCount= */ 3);
-
-        mSharedImageTilesCoordinator.updateTilesCount(4);
-        verifyViews(View.GONE, View.VISIBLE, /* iconViewCount= */ 2);
+        verifyViews(View.VISIBLE, /* iconViewCount= */ 2);
     }
 
     @Test
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesProperties.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesProperties.java
index ae51236b..e3e2a90 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesProperties.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesProperties.java
@@ -18,10 +18,8 @@
             new PropertyModel.WritableIntPropertyKey();
     public static final PropertyModel.WritableIntPropertyKey REMAINING_TILES =
             new PropertyModel.WritableIntPropertyKey();
-    public static final PropertyModel.WritableBooleanPropertyKey SHOW_ADD_BUTTON =
-            new PropertyModel.WritableBooleanPropertyKey();
 
     public static final PropertyKey[] ALL_KEYS = {
-        IS_LOADING, COLOR_THEME, ICON_TILES, REMAINING_TILES, SHOW_ADD_BUTTON
+        IS_LOADING, COLOR_THEME, ICON_TILES, REMAINING_TILES
     };
 }
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesView.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesView.java
index 06c2fb9..0dffcdf5 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesView.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesView.java
@@ -17,12 +17,10 @@
 import androidx.annotation.ColorInt;
 
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
-import org.chromium.ui.widget.ChromeImageButton;
 
 /** View logic for SharedImageTiles component. */
 public class SharedImageTilesView extends LinearLayout {
     private final Context mContext;
-    private ChromeImageButton mButtonTileView;
     private TextView mCountTileView;
     private LinearLayout mLastButtonTileView;
 
@@ -40,7 +38,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mButtonTileView = findViewById(R.id.shared_image_tiles_add);
         mCountTileView = findViewById(R.id.tiles_count);
         mLastButtonTileView = findViewById(R.id.last_tile_container);
     }
@@ -59,8 +56,6 @@
         if (color == SharedImageTilesColor.DYNAMIC) {
             mCountTileView.setTextColor(
                     SemanticColorUtils.getDefaultIconColorOnAccent1Container(mContext));
-            mButtonTileView.setColorFilter(
-                    SemanticColorUtils.getDefaultIconColorOnAccent1Container(mContext));
             setTileBackgroundColor(SemanticColorUtils.getColorPrimaryContainer(mContext));
         }
     }
@@ -71,7 +66,6 @@
         for (int i = 0; i < getChildCount() - 1; i++) {
             removeViewAt(i);
         }
-        mButtonTileView.setVisibility(View.GONE);
         mCountTileView.setVisibility(View.GONE);
         mLastButtonTileView.setVisibility(View.GONE);
 
@@ -92,9 +86,4 @@
                 res.getString(R.string.shared_image_tiles_count, Integer.toString(count));
         mCountTileView.setText(countText);
     }
-
-    void showButtonTile() {
-        mLastButtonTileView.setVisibility(View.VISIBLE);
-        mButtonTileView.setVisibility(View.VISIBLE);
-    }
 }
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesViewBinder.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesViewBinder.java
index 1d49194..cf581bc 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesViewBinder.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesViewBinder.java
@@ -8,7 +8,6 @@
 import static org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesProperties.ICON_TILES;
 import static org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesProperties.IS_LOADING;
 import static org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesProperties.REMAINING_TILES;
-import static org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesProperties.SHOW_ADD_BUTTON;
 
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -28,10 +27,6 @@
             if (model.get(REMAINING_TILES) > 0) {
                 view.showCountTile(model.get(REMAINING_TILES));
             }
-        } else if (SHOW_ADD_BUTTON == propertyKey) {
-            if (model.get(SHOW_ADD_BUTTON)) {
-                view.showButtonTile();
-            }
         }
     }
 }
diff --git a/chrome/browser/dips/dips_bounce_detector.cc b/chrome/browser/dips/dips_bounce_detector.cc
index 552dd87..917c0aac 100644
--- a/chrome/browser/dips/dips_bounce_detector.cc
+++ b/chrome/browser/dips/dips_bounce_detector.cc
@@ -31,13 +31,12 @@
 #include "chrome/browser/dips/dips_service.h"
 #include "chrome/browser/dips/dips_storage.h"
 #include "chrome/browser/dips/dips_utils.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/tpcd/experiment/tpcd_experiment_features.h"
-#include "components/content_settings/browser/page_specific_content_settings.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/cookie_access_details.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/page.h"
+#include "content/public/browser/page_user_data.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -131,6 +130,18 @@
 
 DIPSWebContentsObserver::~DIPSWebContentsObserver() = default;
 
+DIPSWebContentsObserver::Observer::~Observer() {
+  CHECK(!IsInObserverList());
+}
+
+void DIPSWebContentsObserver::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void DIPSWebContentsObserver::RemoveObserver(const Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
 RedirectChainDetector::RedirectChainDetector(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
       content::WebContentsUserData<RedirectChainDetector>(*web_contents),
@@ -591,13 +602,11 @@
   // guaranteed to outlive the call.
   dips_service_->HandleRedirectChain(
       CloneRedirects(redirects), std::make_unique<DIPSRedirectChainInfo>(chain),
-      base::BindRepeating(
-          &DIPSWebContentsObserver::IncrementPageSpecificBounceCount,
-          weak_factory_.GetWeakPtr()));
+      base::BindRepeating(&DIPSWebContentsObserver::OnStatefulBounce,
+                          weak_factory_.GetWeakPtr()));
 }
 
-void DIPSWebContentsObserver::IncrementPageSpecificBounceCount(
-    const GURL& final_url) {
+void DIPSWebContentsObserver::OnStatefulBounce(const GURL& final_url) {
   // Do nothing if the current URL doesn't match the final URL of the chain.
   // This means that the user has navigated away from the bounce destination, so
   // we don't want to update settings for the wrong site.
@@ -605,11 +614,9 @@
     return;
   }
 
-  // TODO: crbug.com/343631048 - move this out of DIPSWebContentsObserver into a
-  // DIPSService::Observer.
-  auto* pscs = content_settings::PageSpecificContentSettings::GetForPage(
-      web_contents()->GetPrimaryPage());
-  pscs->IncrementStatefulBounceCount();
+  for (auto& observer : observers_) {
+    observer.OnStatefulBounce(web_contents());
+  }
 }
 
 // A thin wrapper around NavigationHandle to implement DIPSNavigationHandle.
diff --git a/chrome/browser/dips/dips_bounce_detector.h b/chrome/browser/dips/dips_bounce_detector.h
index 08f9f22..90b32fb 100644
--- a/chrome/browser/dips/dips_bounce_detector.h
+++ b/chrome/browser/dips/dips_bounce_detector.h
@@ -14,6 +14,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
+#include "base/supports_user_data.h"
 #include "base/time/default_clock.h"
 #include "base/timer/timer.h"
 #include "base/types/optional_ref.h"
@@ -38,6 +39,10 @@
 class TickClock;
 }  // namespace base
 
+namespace content {
+class WebContents;
+}
+
 namespace url {
 class Origin;
 }
@@ -439,12 +444,22 @@
       public content::WebContentsUserData<DIPSWebContentsObserver>,
       public content::SharedWorkerService::Observer,
       public content::DedicatedWorkerService::Observer,
-      public RedirectChainDetector::Observer {
+      public RedirectChainDetector::Observer,
+      public base::SupportsUserData {
  public:
   static void MaybeCreateForWebContents(content::WebContents* web_contents);
 
   ~DIPSWebContentsObserver() override;
 
+  class Observer : public base::CheckedObserver {
+   public:
+    ~Observer() override;
+    virtual void OnStatefulBounce(content::WebContents*) {}
+  };
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(const Observer* observer);
+
   // Use the passed handler instead of DIPSWebContentsObserver::EmitDIPSIssue().
   void SetIssueReportingCallbackForTesting(
       DIPSIssueReportingCallback callback) {
@@ -473,7 +488,7 @@
   void RecordEvent(DIPSRecordedEvent event,
                    const GURL& url,
                    const base::Time& time);
-  void IncrementPageSpecificBounceCount(const GURL& final_url);
+  void OnStatefulBounce(const GURL& final_url);
 
   // Start RedirectChainDetector::Observer overrides:
   void ReportRedirectors(const std::set<std::string>& sites) override;
@@ -543,6 +558,7 @@
   std::optional<base::Time> last_storage_timestamp_;
   std::optional<base::Time> last_interaction_timestamp_;
 
+  base::ObserverList<Observer> observers_;
   base::WeakPtrFactory<DIPSWebContentsObserver> weak_factory_{this};
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
diff --git a/chrome/browser/dips/dips_service.cc b/chrome/browser/dips/dips_service.cc
index d533ab28..ce1064e 100644
--- a/chrome/browser/dips/dips_service.cc
+++ b/chrome/browser/dips/dips_service.cc
@@ -344,7 +344,7 @@
 void DIPSServiceImpl::HandleRedirectChain(
     std::vector<DIPSRedirectInfoPtr> redirects,
     DIPSRedirectChainInfoPtr chain,
-    base::RepeatingCallback<void(const GURL&)> content_settings_callback) {
+    base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback) {
   DCHECK_LE(redirects.size(), chain->length);
 
   if (redirects.empty()) {
@@ -378,7 +378,7 @@
       .WithArgs(url)
       .Then(base::BindOnce(&DIPSServiceImpl::GotState,
                            weak_factory_.GetWeakPtr(), std::move(redirects),
-                           std::move(chain), 0, content_settings_callback));
+                           std::move(chain), 0, stateful_bounce_callback));
 }
 
 void DIPSServiceImpl::RecordInteractionForTesting(const GURL& url) {
@@ -399,7 +399,7 @@
     std::vector<DIPSRedirectInfoPtr> redirects,
     DIPSRedirectChainInfoPtr chain,
     size_t index,
-    base::RepeatingCallback<void(const GURL&)> content_settings_callback,
+    base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback,
     const DIPSState url_state) {
   DCHECK_LT(index, redirects.size());
 
@@ -419,7 +419,7 @@
   HandleRedirect(*redirect, *chain,
                  base::BindRepeating(&DIPSServiceImpl::RecordBounce,
                                      base::Unretained(this)),
-                 content_settings_callback);
+                 stateful_bounce_callback);
 
   if (index + 1 >= redirects.size()) {
     // All redirects handled.
@@ -438,7 +438,7 @@
       .Then(base::BindOnce(&DIPSServiceImpl::GotState,
                            weak_factory_.GetWeakPtr(), std::move(redirects),
                            std::move(chain), index + 1,
-                           content_settings_callback));
+                           stateful_bounce_callback));
 }
 
 void DIPSServiceImpl::RecordBounce(
@@ -447,7 +447,7 @@
     const GURL& final_url,
     base::Time time,
     bool stateful,
-    base::RepeatingCallback<void(const GURL&)> content_settings_callback) {
+    base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback) {
   // If the bounced URL has a 3PC exception when embedded under the initial or
   // final URL in the redirect,then clear the tracking site from the DIPS DB, to
   // avoid deleting its storage. The exception overrides any bounces from
@@ -495,10 +495,10 @@
     return;
   }
 
-  // If the bounce is stateful and not excepted by cookie settings, increment
-  // the bounce counter in PageSpecificContentSettings.
+  // If the bounce is stateful and not excepted by cookie settings, run the
+  // callback.
   if (stateful) {
-    content_settings_callback.Run(final_url);
+    stateful_bounce_callback.Run(final_url);
   }
 
   storage_.AsyncCall(&DIPSStorage::RecordBounce).WithArgs(url, time, stateful);
@@ -509,7 +509,7 @@
     const DIPSRedirectInfo& redirect,
     const DIPSRedirectChainInfo& chain,
     RecordBounceCallback record_bounce,
-    base::RepeatingCallback<void(const GURL&)> content_settings_callback) {
+    base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback) {
   bool initial_site_same = (redirect.site == chain.initial_site);
   bool final_site_same = (redirect.site == chain.final_site);
   DCHECK_LT(redirect.chain_index.value(), chain.length);
@@ -545,7 +545,7 @@
         redirect.url.url, redirect.has_3pc_exception.value(),
         chain.final_url.url, redirect.time,
         /*stateful=*/redirect.access_type > SiteDataAccessType::kRead,
-        content_settings_callback);
+        stateful_bounce_callback);
   }
 
   RedirectCategory category =
diff --git a/chrome/browser/dips/dips_service.h b/chrome/browser/dips/dips_service.h
index 75222f1..c919be2 100644
--- a/chrome/browser/dips/dips_service.h
+++ b/chrome/browser/dips/dips_service.h
@@ -73,7 +73,7 @@
       const GURL& final_url,
       base::Time time,
       bool stateful,
-      base::RepeatingCallback<void(const GURL&)> content_settings_callback)>;
+      base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback)>;
 
   ~DIPSServiceImpl() override;
 
@@ -86,9 +86,9 @@
       const GURL& final_url,
       base::Time time,
       bool stateful,
-      base::RepeatingCallback<void(const GURL&)> content_settings_callback) {
+      base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback) {
     RecordBounce(url, has_3pc_exception, final_url, time, stateful,
-                 content_settings_callback);
+                 stateful_bounce_callback);
   }
 
   DIPSCookieMode GetCookieMode() const;
@@ -105,7 +105,7 @@
   void HandleRedirectChain(
       std::vector<DIPSRedirectInfoPtr> redirects,
       DIPSRedirectChainInfoPtr chain,
-      base::RepeatingCallback<void(const GURL&)> content_settings_callback);
+      base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback);
 
   void RecordInteractionForTesting(const GURL& url) override;
 
@@ -164,7 +164,7 @@
       std::vector<DIPSRedirectInfoPtr> redirects,
       DIPSRedirectChainInfoPtr chain,
       size_t index,
-      base::RepeatingCallback<void(const GURL&)> content_settings_callback,
+      base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback,
       const DIPSState url_state);
   void RecordBounce(
       const GURL& url,
@@ -172,12 +172,12 @@
       const GURL& final_url,
       base::Time time,
       bool stateful,
-      base::RepeatingCallback<void(const GURL&)> content_settings_callback);
+      base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback);
   static void HandleRedirect(
       const DIPSRedirectInfo& redirect,
       const DIPSRedirectChainInfo& chain,
       RecordBounceCallback callback,
-      base::RepeatingCallback<void(const GURL&)> content_settings_callback);
+      base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback);
 
   scoped_refptr<base::SequencedTaskRunner> CreateTaskRunner();
 
diff --git a/chrome/browser/dips/dips_service_unittest.cc b/chrome/browser/dips/dips_service_unittest.cc
index 06ba385..ff381af5 100644
--- a/chrome/browser/dips/dips_service_unittest.cc
+++ b/chrome/browser/dips/dips_service_unittest.cc
@@ -75,13 +75,13 @@
       const GURL& final_url,
       base::Time time,
       bool stateful,
-      base::RepeatingCallback<void(const GURL&)> content_settings_callback) {
+      base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback) {
     DIPSServiceImpl::Get(browser_context)
         ->RecordBounceForTesting(url,
                                  Has3pcException(browser_context, nullptr, url,
                                                  initial_url, final_url),
                                  final_url, time, stateful,
-                                 content_settings_callback);
+                                 stateful_bounce_callback);
   }
 
  private:
@@ -313,11 +313,11 @@
       const GURL& final_url,
       base::Time time,
       bool stateful,
-      base::RepeatingCallback<void(const GURL&)> content_settings_callback) {
+      base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback) {
     GetService()->RecordBounceForTesting(
         url,
         Has3pcException(GetProfile(), nullptr, url, initial_url, final_url),
-        final_url, time, stateful, content_settings_callback);
+        final_url, time, stateful, stateful_bounce_callback);
   }
 
  private:
diff --git a/chrome/browser/dips/stateful_bounce_counter.cc b/chrome/browser/dips/stateful_bounce_counter.cc
new file mode 100644
index 0000000..e60751c
--- /dev/null
+++ b/chrome/browser/dips/stateful_bounce_counter.cc
@@ -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.
+
+#include "chrome/browser/dips/stateful_bounce_counter.h"
+
+#include <memory>
+
+#include "chrome/browser/dips/dips_bounce_detector.h"
+#include "components/content_settings/browser/page_specific_content_settings.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace dips {
+
+StatefulBounceCounter::StatefulBounceCounter(PassKey,
+                                             DIPSWebContentsObserver* dips_wco)
+    : dips_wco_(dips_wco) {
+  dips_wco_->AddObserver(this);
+}
+
+StatefulBounceCounter::~StatefulBounceCounter() {
+  dips_wco_->RemoveObserver(this);
+}
+
+/*static*/
+StatefulBounceCounter* StatefulBounceCounter::Get(
+    DIPSWebContentsObserver* dips_wco) {
+  if (void* data = dips_wco->GetUserData(&kUserDataKey)) {
+    return static_cast<StatefulBounceCounter*>(data);
+  }
+  auto counter = std::make_unique<StatefulBounceCounter>(PassKey(), dips_wco);
+  StatefulBounceCounter* p = counter.get();  // grab a pointer before moving it.
+  dips_wco->SetUserData(&kUserDataKey, std::move(counter));
+  return p;
+}
+
+void StatefulBounceCounter::OnStatefulBounce(
+    content::WebContents* web_contents) {
+  auto* pscs = content_settings::PageSpecificContentSettings::GetForPage(
+      web_contents->GetPrimaryPage());
+  pscs->IncrementStatefulBounceCount();
+}
+
+const int StatefulBounceCounter::kUserDataKey;
+
+}  // namespace dips
diff --git a/chrome/browser/dips/stateful_bounce_counter.h b/chrome/browser/dips/stateful_bounce_counter.h
new file mode 100644
index 0000000..fda96bb
--- /dev/null
+++ b/chrome/browser/dips/stateful_bounce_counter.h
@@ -0,0 +1,42 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_DIPS_STATEFUL_BOUNCE_COUNTER_H_
+#define CHROME_BROWSER_DIPS_STATEFUL_BOUNCE_COUNTER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/supports_user_data.h"
+#include "base/types/pass_key.h"
+#include "chrome/browser/dips/dips_bounce_detector.h"
+#include "chrome/browser/dips/dips_service.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace dips {
+
+// This class exists just to call
+// PageSpecificContentSettings::IncrementStatefulBounceCount() whenever the user
+// is statefully bounced.
+class StatefulBounceCounter : public DIPSWebContentsObserver::Observer,
+                              public base::SupportsUserData::Data {
+ public:
+  using PassKey = base::PassKey<StatefulBounceCounter>;
+  // The constructor takes a PassKey so only Get() can call std::make_unique().
+  StatefulBounceCounter(PassKey, DIPSWebContentsObserver*);
+  ~StatefulBounceCounter() override;
+
+  // Get the instance for `dips_wco`, creating it if it doesn't exist yet.
+  static StatefulBounceCounter* Get(DIPSWebContentsObserver* dips_wco);
+
+  void OnStatefulBounce(content::WebContents*) override;
+
+ private:
+  raw_ptr<DIPSWebContentsObserver> dips_wco_;
+
+  // For SupportsUserData:
+  static const int kUserDataKey = 0;
+};
+
+}  // namespace dips
+
+#endif  // CHROME_BROWSER_DIPS_STATEFUL_BOUNCE_COUNTER_H_
diff --git a/chrome/browser/educational_tip/BUILD.gn b/chrome/browser/educational_tip/BUILD.gn
index 043ccb37..1408254 100644
--- a/chrome/browser/educational_tip/BUILD.gn
+++ b/chrome/browser/educational_tip/BUILD.gn
@@ -6,6 +6,7 @@
 
 android_library("java") {
   sources = [
+    "java/src/org/chromium/chrome/browser/educational_tip/EducationTipModuleActionDelegate.java",
     "java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProvider.java",
     "java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java",
     "java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilder.java",
@@ -16,6 +17,7 @@
     "java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleViewBinder.java",
     "java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoBottomSheetContent.java",
     "java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java",
+    "java/src/org/chromium/chrome/browser/educational_tip/cards/QuickDeletePromoCoordinator.java",
     "java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupPromoCoordinator.java",
     "java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupSyncPromoCoordinator.java",
   ]
@@ -42,6 +44,7 @@
   sources = [
     "java/res/drawable/default_browser_promo_logo.xml",
     "java/res/drawable/educational_tip_module_content_image_background.xml",
+    "java/res/drawable/quick_delete_promo_logo.xml",
     "java/res/drawable/tab_group_promo_logo.xml",
     "java/res/drawable/tab_group_sync_promo_logo.xml",
     "java/res/layout/educational_tip_default_browser_bottom_sheet.xml",
@@ -63,6 +66,7 @@
     "junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleViewBinderUnitTest.java",
+    "junit/src/org/chromium/chrome/browser/educational_tip/QuickDeletePromoCoordinatorUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/TabGroupPromoCoordinatorUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/TabGroupSyncPromoCoordinatorUnitTest.java",
   ]
diff --git a/chrome/browser/educational_tip/java/res/drawable/quick_delete_promo_logo.xml b/chrome/browser/educational_tip/java/res/drawable/quick_delete_promo_logo.xml
new file mode 100644
index 0000000..09cfd25
--- /dev/null
+++ b/chrome/browser/educational_tip/java/res/drawable/quick_delete_promo_logo.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2024 The Chromium Authors.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<vector android:autoMirrored="true" android:height="72dp"
+    android:viewportHeight="72" android:viewportWidth="72"
+    android:width="72dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillAlpha="0.05" android:fillColor="#6991D6" android:pathData="M11.31,0L60.69,0A11.31,11.31 0,0 1,72 11.31L72,60.69A11.31,11.31 0,0 1,60.69 72L11.31,72A11.31,11.31 0,0 1,0 60.69L0,11.31A11.31,11.31 0,0 1,11.31 0z"/>
+    <path android:fillColor="@color/material_primary_80" android:pathData="M31.9,61.291a7.309,12.076 66.624,1 0,22.17 -9.583a7.309,12.076 66.624,1 0,-22.17 9.583z"/>
+    <path android:fillColor="@color/material_primary_90" android:fillType="evenOdd" android:pathData="M48,37.5H24L25.5,61.5C25.5,63.985 30.201,66 36,66C41.799,66 46.5,63.985 46.5,61.5L48,37.5Z"/>
+    <group>
+        <clip-path android:fillType="evenOdd" android:pathData="M48,37.5H24L25.5,61.5C25.5,63.985 30.201,66 36,66C41.799,66 46.5,63.985 46.5,61.5L48,37.5Z"/>
+        <path android:fillColor="@color/material_primary_40" android:pathData="M24,37.5V35.5H21.871L22.004,37.625L24,37.5ZM48,37.5L49.996,37.625L50.129,35.5H48V37.5ZM25.5,61.5H27.5V61.438L27.496,61.375L25.5,61.5ZM46.5,61.5L44.504,61.375L44.5,61.438V61.5H46.5ZM24,39.5H48V35.5H24V39.5ZM27.496,61.375L25.996,37.375L22.004,37.625L23.504,61.625L27.496,61.375ZM36,64C33.305,64 30.96,63.528 29.363,62.844C28.561,62.5 28.04,62.142 27.749,61.847C27.465,61.56 27.5,61.445 27.5,61.5H23.5C23.5,62.798 24.122,63.867 24.902,64.657C25.673,65.438 26.689,66.05 27.788,66.52C29.991,67.465 32.896,68 36,68V64ZM44.5,61.5C44.5,61.445 44.535,61.56 44.251,61.847C43.96,62.142 43.439,62.5 42.637,62.844C41.04,63.528 38.695,64 36,64V68C39.104,68 42.009,67.465 44.213,66.52C45.311,66.05 46.327,65.438 47.098,64.657C47.878,63.867 48.5,62.798 48.5,61.5H44.5ZM46.004,37.375L44.504,61.375L48.496,61.625L49.996,37.625L46.004,37.375Z"/>
+    </group>
+    <path android:fillColor="@color/material_primary_40" android:pathData="M24,37.5a12,4.5 0,1 0,24 0a12,4.5 0,1 0,-24 0z"/>
+    <path android:fillColor="@android:color/transparent"
+        android:pathData="M30,45L30.75,58.5"
+        android:strokeColor="@color/material_primary_40" android:strokeLineCap="round" android:strokeWidth="1.5"/>
+    <path android:fillColor="@android:color/transparent"
+        android:pathData="M42,45L41.25,58.5"
+        android:strokeColor="@color/material_primary_40" android:strokeLineCap="round" android:strokeWidth="1.5"/>
+    <path android:fillColor="@android:color/transparent" android:pathData="M36,46.5V60"
+        android:strokeColor="@color/material_primary_40" android:strokeLineCap="round" android:strokeWidth="1.5"/>
+    <path android:fillColor="@color/material_primary_80" android:pathData="M25.597,26.524L41.563,14.48A2,2 117.123,0 1,44.364 14.872L48.58,20.46A2,2 105.176,0 1,48.188 23.261L32.221,35.306A2,2 77.399,0 1,29.42 34.914L25.204,29.325A2,2 97.973,0 1,25.597 26.524z"/>
+    <path android:fillColor="@color/material_primary_40" android:pathData="M29.007,30.215L44.175,18.772A0.5,0.5 86.422,0 1,44.875 18.871L44.875,18.871A0.5,0.5 86.422,0 1,44.777 19.571L29.609,31.013A0.5,0.5 91.751,0 1,28.909 30.915L28.909,30.915A0.5,0.5 91.751,0 1,29.007 30.215z"/>
+    <path android:fillColor="@color/material_primary_40" android:pathData="M27.2,27.82L42.368,16.377A0.5,0.5 88.763,0 1,43.069 16.476L43.069,16.476A0.5,0.5 88.763,0 1,42.971 17.176L27.802,28.618A0.5,0.5 50.004,0 1,27.102 28.52L27.102,28.52A0.5,0.5 50.004,0 1,27.2 27.82z"/>
+    <path android:fillColor="@color/material_primary_40" android:pathData="M30.814,32.61L40.56,25.258A0.5,0.5 101.104,0 1,41.26 25.356L41.26,25.356A0.5,0.5 101.104,0 1,41.162 26.056L31.416,33.408A0.5,0.5 115.899,0 1,30.716 33.31L30.716,33.31A0.5,0.5 115.899,0 1,30.814 32.61z"/>
+    <path android:fillColor="@color/material_primary_40" android:fillType="evenOdd" android:pathData="M45.746,16.704L33.353,20.673L41.563,14.48C42.445,13.814 43.699,13.99 44.364,14.872L45.746,16.704Z"/>
+    <path android:fillColor="@color/material_primary_80" android:pathData="M32.558,10.958L45.088,4.598A2,2 120.262,0 1,47.776 5.476L49.587,9.043A2,2 119.213,0 1,48.709 11.731L36.179,18.092A2,2 54.992,0 1,33.49 17.213L31.68,13.647A2,2 95.655,0 1,32.558 10.958z"/>
+    <path android:fillColor="@color/material_primary_60" android:pathData="M33.909,12.515L40.721,9.057A0.5,0.5 57.401,0 1,41.393 9.277L41.393,9.277A0.5,0.5 57.401,0 1,41.174 9.949L34.361,13.407A0.5,0.5 113.708,0 1,33.689 13.187L33.689,13.187A0.5,0.5 113.708,0 1,33.909 12.515z"/>
+    <path android:fillColor="@color/material_primary_60" android:pathData="M35.267,15.19L46.645,9.415A0.5,0.5 124.403,0 1,47.317 9.634L47.317,9.634A0.5,0.5 124.403,0 1,47.097 10.306L35.719,16.082A0.5,0.5 131.437,0 1,35.047 15.862L35.047,15.862A0.5,0.5 131.437,0 1,35.267 15.19z"/>
+    <path android:fillColor="@color/material_primary_80" android:pathData="M44.357,27.747L37.49,37.587A2,2 73.997,0 0,37.986 40.372L38.806,40.944A2,2 73.997,0 0,41.591 40.449L48.458,30.608A2,2 73.997,0 0,47.962 27.823L47.142,27.251A2,2 73.997,0 0,44.357 27.747z"/>
+    <path android:fillColor="@color/material_primary_60" android:pathData="M45.712,29.301L39.417,38.322A0.5,0.5 79.938,0 0,39.541 39.018L39.541,39.018A0.5,0.5 79.938,0 0,40.237 38.894L46.532,29.874A0.5,0.5 79.938,0 0,46.408 29.177L46.408,29.177A0.5,0.5 79.938,0 0,45.712 29.301z"/>
+    <group>
+        <clip-path android:pathData="M23,37h26v7h-26z"/>
+        <path android:fillColor="@android:color/transparent"
+            android:pathData="M47,37.5C47,37.733 46.878,38.074 46.394,38.503C45.91,38.932 45.154,39.363 44.134,39.746C42.101,40.508 39.225,41 36,41C32.775,41 29.899,40.508 27.866,39.746C26.846,39.363 26.09,38.932 25.606,38.503C25.122,38.074 25,37.733 25,37.5C25,37.268 25.122,36.926 25.606,36.497C26.09,36.068 26.846,35.637 27.866,35.254C29.899,34.492 32.775,34 36,34C39.225,34 42.101,34.492 44.134,35.254C45.154,35.637 45.91,36.068 46.394,36.497C46.878,36.926 47,37.268 47,37.5Z"
+            android:strokeColor="@color/material_primary_40" android:strokeWidth="2"/>
+    </group>
+</vector>
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationTipModuleActionDelegate.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationTipModuleActionDelegate.java
new file mode 100644
index 0000000..a90e29d
--- /dev/null
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationTipModuleActionDelegate.java
@@ -0,0 +1,37 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.educational_tip;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.chrome.browser.hub.PaneId;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+
+/** The delegate to provide actions for the educational tips module. */
+public interface EducationTipModuleActionDelegate {
+
+    /** Gets the application context. */
+    @NonNull
+    Context getContext();
+
+    /** Gets the instance of {@link BottomSheetController}. */
+    @NonNull
+    BottomSheetController getBottomSheetController();
+
+    /**
+     * Opens the Hub layout and displays a specific pane.
+     *
+     * @param paneId The id of the pane to show.
+     */
+    void openHubPane(@PaneId int paneId);
+
+    /** Opens the grid tab switcher and show the tab grip Iph dialog. */
+    void openTabGroupIphDialog();
+
+    /** Opens the app menu and highlights the quick delete menu item. */
+    void openAndHighlightQuickDeleteMenuItem();
+}
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java
index 36b2769..83d1365 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipCardProviderFactory.java
@@ -4,21 +4,14 @@
 
 package org.chromium.chrome.browser.educational_tip;
 
-import android.content.Context;
-import android.view.ViewGroup;
-
 import androidx.annotation.NonNull;
 
 import org.chromium.base.CallbackController;
-import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.EducationalTipCardType;
-import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.ShowHubPaneCallback;
 import org.chromium.chrome.browser.educational_tip.cards.DefaultBrowserPromoCoordinator;
+import org.chromium.chrome.browser.educational_tip.cards.QuickDeletePromoCoordinator;
 import org.chromium.chrome.browser.educational_tip.cards.TabGroupPromoCoordinator;
 import org.chromium.chrome.browser.educational_tip.cards.TabGroupSyncPromoCoordinator;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
-import org.chromium.ui.modaldialog.ModalDialogManager;
 
 /** A factory interface for building a EducationalTipCardProvider instance. */
 public class EducationalTipCardProviderFactory {
@@ -26,47 +19,25 @@
      * @return An instance of EducationalTipCardProvider.
      */
     static EducationalTipCardProvider createInstance(
-            @NonNull Context context,
             @EducationalTipCardType int cardType,
             @NonNull Runnable onModuleClickedCallback,
-            @NonNull BottomSheetController bottomSheetController) {
-        if (cardType == EducationalTipCardType.DEFAULT_BROWSER_PROMO) {
-            return new DefaultBrowserPromoCoordinator(
-                    context, onModuleClickedCallback, bottomSheetController);
+            @NonNull CallbackController callbackController,
+            @NonNull EducationTipModuleActionDelegate actionDelegate) {
+        switch (cardType) {
+            case EducationalTipCardType.DEFAULT_BROWSER_PROMO:
+                return new DefaultBrowserPromoCoordinator(onModuleClickedCallback, actionDelegate);
+            case EducationalTipCardType.TAB_GROUPS:
+                return new TabGroupPromoCoordinator(
+                        onModuleClickedCallback, callbackController, actionDelegate);
+            case EducationalTipCardType.TAB_GROUP_SYNC:
+                return new TabGroupSyncPromoCoordinator(
+                        onModuleClickedCallback, callbackController, actionDelegate);
+            case EducationalTipCardType.QUICK_DELETE:
+                return new QuickDeletePromoCoordinator(
+                        onModuleClickedCallback, callbackController, actionDelegate);
+            default:
+                assert false : "Educational tip module's card type not supported!";
+                return null;
         }
-
-        assert false : "Educational tip module's card type not supported!";
-        return null;
-    }
-
-    /**
-     * @return An instance of TabGroupPromoCoordinator.
-     */
-    static TabGroupPromoCoordinator createInstance(
-            @NonNull Context context,
-            @NonNull Runnable onModuleClickedCallback,
-            @NonNull CallbackController callbackController,
-            @NonNull ObservableSupplier<ModalDialogManager> modalDialogManagerSupplier,
-            @NonNull ShowHubPaneCallback showHubPaneCallback,
-            @NonNull Supplier<ViewGroup> parentViewSupplier) {
-        return new TabGroupPromoCoordinator(
-                context,
-                onModuleClickedCallback,
-                callbackController,
-                modalDialogManagerSupplier,
-                showHubPaneCallback,
-                parentViewSupplier);
-    }
-
-    /**
-     * @return An instance of TabGroupPromoSyncCoordinator.
-     */
-    static TabGroupSyncPromoCoordinator createInstance(
-            @NonNull Context context,
-            @NonNull Runnable onModuleClickedCallback,
-            @NonNull CallbackController callbackController,
-            @NonNull ShowHubPaneCallback showHubPaneCallback) {
-        return new TabGroupSyncPromoCoordinator(
-                context, onModuleClickedCallback, callbackController, showHubPaneCallback);
     }
 }
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilder.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilder.java
index 7e1fe066..ebf5a6db 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilder.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilder.java
@@ -4,45 +4,26 @@
 
 package org.chromium.chrome.browser.educational_tip;
 
-import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
 
 import org.chromium.base.Callback;
-import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.ShowHubPaneCallback;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.magic_stack.ModuleConfigChecker;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate;
 import org.chromium.chrome.browser.magic_stack.ModuleProvider;
 import org.chromium.chrome.browser.magic_stack.ModuleProviderBuilder;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
-import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
 public class EducationalTipModuleBuilder implements ModuleProviderBuilder, ModuleConfigChecker {
-    private final Context mContext;
-    private final BottomSheetController mBottomSheetController;
-    private final ObservableSupplier<ModalDialogManager> mModalDialogManagerSupplier;
-    private final ShowHubPaneCallback mShowHubPaneCallback;
-    private final Supplier<ViewGroup> mParentViewSupplier;
+    private final EducationTipModuleActionDelegate mActionDelegate;
 
     /** Pass in the dependencies needed to build {@link EducationalTipModuleCoordinator}. */
-    public EducationalTipModuleBuilder(
-            @NonNull Context context,
-            @NonNull BottomSheetController bottomSheetController,
-            @NonNull ObservableSupplier<ModalDialogManager> modalDialogManagerSupplier,
-            @NonNull ShowHubPaneCallback showHubPaneCallback,
-            @NonNull Supplier<ViewGroup> parentViewSupplier) {
-        mContext = context;
-        mBottomSheetController = bottomSheetController;
-        mModalDialogManagerSupplier = modalDialogManagerSupplier;
-        mShowHubPaneCallback = showHubPaneCallback;
-        mParentViewSupplier = parentViewSupplier;
+    public EducationalTipModuleBuilder(@NonNull EducationTipModuleActionDelegate actionDelegate) {
+        mActionDelegate = actionDelegate;
     }
 
     /** Build {@link ModuleProvider} for the educational tip module. */
@@ -55,13 +36,7 @@
         }
 
         EducationalTipModuleCoordinator coordinator =
-                new EducationalTipModuleCoordinator(
-                        mContext,
-                        moduleDelegate,
-                        mBottomSheetController,
-                        mModalDialogManagerSupplier,
-                        mShowHubPaneCallback,
-                        mParentViewSupplier);
+                new EducationalTipModuleCoordinator(moduleDelegate, mActionDelegate);
         onModuleBuiltCallback.onResult(coordinator);
         return true;
     }
@@ -70,7 +45,7 @@
     @Override
     public ViewGroup createView(@NonNull ViewGroup parentView) {
         return (ViewGroup)
-                LayoutInflater.from(mContext)
+                LayoutInflater.from(mActionDelegate.getContext())
                         .inflate(R.layout.educational_tip_module_layout, parentView, false);
     }
 
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleCoordinator.java
index f657193..ad868549 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleCoordinator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleCoordinator.java
@@ -5,17 +5,11 @@
 package org.chromium.chrome.browser.educational_tip;
 
 import android.content.Context;
-import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
 
-import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.ShowHubPaneCallback;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate;
 import org.chromium.chrome.browser.magic_stack.ModuleProvider;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
-import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Coordinator for the educational tip module. */
@@ -23,22 +17,10 @@
     private final EducationalTipModuleMediator mMediator;
 
     public EducationalTipModuleCoordinator(
-            @NonNull Context context,
             @NonNull ModuleDelegate moduleDelegate,
-            @NonNull BottomSheetController bottomSheetController,
-            @NonNull ObservableSupplier<ModalDialogManager> modalDialogManagerSupplier,
-            @NonNull ShowHubPaneCallback showHubPaneCallback,
-            @NonNull Supplier<ViewGroup> parentViewSupplier) {
+            @NonNull EducationTipModuleActionDelegate actionDelegate) {
         PropertyModel model = new PropertyModel(EducationalTipModuleProperties.ALL_KEYS);
-        mMediator =
-                new EducationalTipModuleMediator(
-                        context,
-                        model,
-                        moduleDelegate,
-                        bottomSheetController,
-                        modalDialogManagerSupplier,
-                        showHubPaneCallback,
-                        parentViewSupplier);
+        mMediator = new EducationalTipModuleMediator(model, moduleDelegate, actionDelegate);
     }
 
     // ModuleProvider implementation.
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediator.java
index f5c5d82b..12df0fe 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediator.java
@@ -4,85 +4,46 @@
 
 package org.chromium.chrome.browser.educational_tip;
 
-import android.content.Context;
-import android.view.ViewGroup;
-
 import androidx.annotation.NonNull;
 
 import org.chromium.base.CallbackController;
-import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.EducationalTipCardType;
-import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.ShowHubPaneCallback;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
-import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Mediator for the educational tip module. */
 public class EducationalTipModuleMediator {
     private static final String FORCE_TAB_GROUP = "force_tab_group";
     private static final String FORCE_TAB_GROUP_SYNC = "force_tab_group_sync";
+    private static final String FORCE_QUICK_DELETE = "force_quick_delete";
 
-    private final Context mContext;
+    private final EducationTipModuleActionDelegate mActionDelegate;
     private final @ModuleType int mModuleType;
     private final PropertyModel mModel;
     private final ModuleDelegate mModuleDelegate;
-    private final BottomSheetController mBottomSheetController;
-    private final ObservableSupplier<ModalDialogManager> mModalDialogManagerSupplier;
-    private final ShowHubPaneCallback mShowHubPaneCallback;
-    private final Supplier<ViewGroup> mParentViewSupplier;
     private final CallbackController mCallbackController;
 
     private EducationalTipCardProvider mEducationalTipCardProvider;
 
     EducationalTipModuleMediator(
-            @NonNull Context context,
             @NonNull PropertyModel model,
             @NonNull ModuleDelegate moduleDelegate,
-            @NonNull BottomSheetController bottomSheetController,
-            @NonNull ObservableSupplier<ModalDialogManager> modalDialogManagerSupplier,
-            @NonNull ShowHubPaneCallback showHubPaneCallback,
-            @NonNull Supplier<ViewGroup> parentViewSupplier) {
-        mContext = context;
+            EducationTipModuleActionDelegate actionDelegate) {
         mModuleType = ModuleType.EDUCATIONAL_TIP;
         mModel = model;
         mModuleDelegate = moduleDelegate;
-        mBottomSheetController = bottomSheetController;
-        mModalDialogManagerSupplier = modalDialogManagerSupplier;
-        mShowHubPaneCallback = showHubPaneCallback;
-        mParentViewSupplier = parentViewSupplier;
+        mActionDelegate = actionDelegate;
 
         mCallbackController = new CallbackController();
     }
 
     /** Show the educational tip module. */
     void showModule() {
-        @EducationalTipCardType int type = getCardType();
-
-        if (type == EducationalTipCardType.TAB_GROUPS) {
-            mEducationalTipCardProvider =
-                    EducationalTipCardProviderFactory.createInstance(
-                            mContext,
-                            this::removeModule,
-                            mCallbackController,
-                            mModalDialogManagerSupplier,
-                            mShowHubPaneCallback,
-                            mParentViewSupplier);
-        } else if (type == EducationalTipCardType.TAB_GROUP_SYNC) {
-            mEducationalTipCardProvider =
-                    EducationalTipCardProviderFactory.createInstance(
-                            mContext,
-                            this::removeModule,
-                            mCallbackController,
-                            mShowHubPaneCallback);
-        } else {
-            mEducationalTipCardProvider =
-                    EducationalTipCardProviderFactory.createInstance(
-                            mContext, getCardType(), this::removeModule, mBottomSheetController);
-        }
+        mEducationalTipCardProvider =
+                EducationalTipCardProviderFactory.createInstance(
+                        getCardType(), this::removeModule, mCallbackController, mActionDelegate);
 
         mModel.set(
                 EducationalTipModuleProperties.MODULE_CONTENT_TITLE_STRING,
@@ -111,6 +72,9 @@
         } else if (ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
                 ChromeFeatureList.EDUCATIONAL_TIP_MODULE, FORCE_TAB_GROUP_SYNC, false)) {
             return EducationalTipCardType.TAB_GROUP_SYNC;
+        } else if (ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                ChromeFeatureList.EDUCATIONAL_TIP_MODULE, FORCE_QUICK_DELETE, false)) {
+            return EducationalTipCardType.QUICK_DELETE;
         }
         return EducationalTipCardType.DEFAULT_BROWSER_PROMO;
     }
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java
index 1cacff9..dfd193b 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/DefaultBrowserPromoCoordinator.java
@@ -14,6 +14,7 @@
 import androidx.annotation.NonNull;
 
 import org.chromium.base.IntentUtils;
+import org.chromium.chrome.browser.educational_tip.EducationTipModuleActionDelegate;
 import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider;
 import org.chromium.chrome.browser.educational_tip.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -22,32 +23,32 @@
 
 /** Coordinator for the default browser promo card. */
 public class DefaultBrowserPromoCoordinator implements EducationalTipCardProvider {
-    private final Context mContext;
-
     // For the default browser promo card specifically, it is triggered only when the user clicks on
     // the bottom sheet, directing them to the default app settings page.
     private final Runnable mOnModuleClickedCallback;
+    private final EducationTipModuleActionDelegate mActionDelegate;
 
-    private final BottomSheetController mBottomSheetController;
     private DefaultBrowserPromoBottomSheetContent mDefaultBrowserBottomSheetContent;
 
     public DefaultBrowserPromoCoordinator(
-            @NonNull Context context,
             @NonNull Runnable onModuleClickedCallback,
-            @NonNull BottomSheetController bottomSheetController) {
-        mContext = context;
+            @NonNull EducationTipModuleActionDelegate actionDelegate) {
         mOnModuleClickedCallback = onModuleClickedCallback;
-        mBottomSheetController = bottomSheetController;
+        mActionDelegate = actionDelegate;
     }
 
     @Override
     public String getCardTitle() {
-        return mContext.getString(R.string.educational_tip_default_browser_title);
+        return mActionDelegate
+                .getContext()
+                .getString(R.string.educational_tip_default_browser_title);
     }
 
     @Override
     public String getCardDescription() {
-        return mContext.getString(R.string.educational_tip_default_browser_description);
+        return mActionDelegate
+                .getContext()
+                .getString(R.string.educational_tip_default_browser_description);
     }
 
     @Override
@@ -57,21 +58,23 @@
 
     @Override
     public void onCardClicked() {
+        Context context = mActionDelegate.getContext();
         View defaultBrowserBottomSheetView =
-                LayoutInflater.from(mContext)
+                LayoutInflater.from(context)
                         .inflate(
                                 R.layout.educational_tip_default_browser_bottom_sheet,
                                 /* root= */ null);
         mDefaultBrowserBottomSheetContent =
                 new DefaultBrowserPromoBottomSheetContent(defaultBrowserBottomSheetView);
-        mBottomSheetController.requestShowContent(mDefaultBrowserBottomSheetContent, true);
+        BottomSheetController bottomSheetController = mActionDelegate.getBottomSheetController();
+        bottomSheetController.requestShowContent(mDefaultBrowserBottomSheetContent, true);
         ButtonCompat bottomSheetButton =
                 defaultBrowserBottomSheetView.findViewById(
                         R.id.default_browser_bottom_sheet_button);
         bottomSheetButton.setOnClickListener(
                 (v) -> {
-                    IntentUtils.safeStartActivity(mContext, createBottomSheetOnClickIntent());
-                    mBottomSheetController.hideContent(
+                    IntentUtils.safeStartActivity(context, createBottomSheetOnClickIntent());
+                    bottomSheetController.hideContent(
                             mDefaultBrowserBottomSheetContent,
                             /* animate= */ true,
                             StateChangeReason.INTERACTION_COMPLETE);
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/QuickDeletePromoCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/QuickDeletePromoCoordinator.java
new file mode 100644
index 0000000..737597c
--- /dev/null
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/QuickDeletePromoCoordinator.java
@@ -0,0 +1,62 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.educational_tip.cards;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+
+import org.chromium.base.CallbackController;
+import org.chromium.chrome.browser.educational_tip.EducationTipModuleActionDelegate;
+import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider;
+import org.chromium.chrome.browser.educational_tip.R;
+
+/** Coordinator for the quick delete promo card. */
+public class QuickDeletePromoCoordinator implements EducationalTipCardProvider {
+    private final EducationTipModuleActionDelegate mActionDelegate;
+    private final Runnable mOnClickedRunnable;
+
+    /**
+     * @param onModuleClickedCallback The callback to be called when the module is clicked.
+     * @param callbackController The instance of {@link CallbackController}.
+     * @param actionDelegate The instance of {@link EducationTipModuleActionDelegate}.
+     */
+    public QuickDeletePromoCoordinator(
+            @NonNull Runnable onModuleClickedCallback,
+            @NonNull CallbackController callbackController,
+            @NonNull EducationTipModuleActionDelegate actionDelegate) {
+        mActionDelegate = actionDelegate;
+
+        mOnClickedRunnable =
+                callbackController.makeCancelable(
+                        () -> {
+                            mActionDelegate.openAndHighlightQuickDeleteMenuItem();
+                            onModuleClickedCallback.run();
+                        });
+    }
+
+    // EducationalTipCardProvider implementation.
+
+    @Override
+    public String getCardTitle() {
+        return mActionDelegate.getContext().getString(R.string.educational_tip_quick_delete_title);
+    }
+
+    @Override
+    public String getCardDescription() {
+        return mActionDelegate
+                .getContext()
+                .getString(R.string.educational_tip_quick_delete_description);
+    }
+
+    @Override
+    public @DrawableRes int getCardImage() {
+        return R.drawable.quick_delete_promo_logo;
+    }
+
+    @Override
+    public void onCardClicked() {
+        mOnClickedRunnable.run();
+    }
+}
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupPromoCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupPromoCoordinator.java
index 2a3a625..39507f2 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupPromoCoordinator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupPromoCoordinator.java
@@ -4,65 +4,34 @@
 
 package org.chromium.chrome.browser.educational_tip.cards;
 
-import android.content.Context;
-import android.view.ViewGroup;
-
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import org.chromium.base.CallbackController;
-import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.Supplier;
+import org.chromium.chrome.browser.educational_tip.EducationTipModuleActionDelegate;
 import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider;
 import org.chromium.chrome.browser.educational_tip.R;
-import org.chromium.chrome.browser.hub.PaneId;
-import org.chromium.chrome.browser.tab_ui.TabGridIphDialogCoordinator;
-import org.chromium.ui.modaldialog.ModalDialogManager;
 
 /** Coordinator for the Tab group promo card. */
 public class TabGroupPromoCoordinator implements EducationalTipCardProvider {
-    private final Context mContext;
-
-    private final ObservableSupplier<ModalDialogManager> mModalDialogManagerSupplier;
-    private final ShowHubPaneCallback mShowHubPaneCallback;
-    private final Supplier<ViewGroup> mParentViewSupplier;
+    private final EducationTipModuleActionDelegate mActionDelegate;
     private final Runnable mOnClickedRunnable;
-    private CallbackController mCallbackController;
-    @Nullable private TabGridIphDialogCoordinator mTabGridIphDialogCoordinator;
 
     /**
-     * @param context The Context of the application.
      * @param onModuleClickedCallback The callback to be called when the module is clicked.
-     * @param modalDialogManagerSupplier The supplier of {@link ModalDialogManager} instance.
-     * @param showHubPaneCallback The callback to open a Hub pane.
-     * @param parentViewSupplier The supplier of the parent view.
+     * @param callbackController The instance of {@link CallbackController}.
+     * @param actionDelegate The instance of {@link EducationTipModuleActionDelegate}.
      */
     public TabGroupPromoCoordinator(
-            @NonNull Context context,
             @NonNull Runnable onModuleClickedCallback,
             @NonNull CallbackController callbackController,
-            @NonNull ObservableSupplier<ModalDialogManager> modalDialogManagerSupplier,
-            @NonNull ShowHubPaneCallback showHubPaneCallback,
-            @NonNull Supplier<ViewGroup> parentViewSupplier) {
-        mContext = context;
-        mCallbackController = callbackController;
-        mModalDialogManagerSupplier = modalDialogManagerSupplier;
-        mShowHubPaneCallback = showHubPaneCallback;
-        mParentViewSupplier = parentViewSupplier;
+            @NonNull EducationTipModuleActionDelegate actionDelegate) {
+        mActionDelegate = actionDelegate;
 
         mOnClickedRunnable =
-                mCallbackController.makeCancelable(
+                callbackController.makeCancelable(
                         () -> {
-                            if (mTabGridIphDialogCoordinator == null) {
-                                mTabGridIphDialogCoordinator =
-                                        new TabGridIphDialogCoordinator(
-                                                mContext, mModalDialogManagerSupplier.get());
-                                mTabGridIphDialogCoordinator.setParentView(
-                                        mParentViewSupplier.get());
-                            }
-                            mShowHubPaneCallback.onClick(PaneId.TAB_SWITCHER);
-                            mTabGridIphDialogCoordinator.showIph();
+                            mActionDelegate.openTabGroupIphDialog();
                             onModuleClickedCallback.run();
                         });
     }
@@ -70,12 +39,14 @@
     // EducationalTipCardProvider implementation.
     @Override
     public String getCardTitle() {
-        return mContext.getString(R.string.educational_tip_tab_group_title);
+        return mActionDelegate.getContext().getString(R.string.educational_tip_tab_group_title);
     }
 
     @Override
     public String getCardDescription() {
-        return mContext.getString(R.string.educational_tip_tab_group_description);
+        return mActionDelegate
+                .getContext()
+                .getString(R.string.educational_tip_tab_group_description);
     }
 
     @Override
@@ -87,16 +58,4 @@
     public void onCardClicked() {
         mOnClickedRunnable.run();
     }
-
-    @Override
-    public void destroy() {
-        if (mTabGridIphDialogCoordinator != null) {
-            mTabGridIphDialogCoordinator.setParentView(null);
-        }
-    }
-
-    public void setTabGridIphDialogCoordinatorForTesting(
-            TabGridIphDialogCoordinator tabGridIphDialogCoordinator) {
-        mTabGridIphDialogCoordinator = tabGridIphDialogCoordinator;
-    }
 }
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupSyncPromoCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupSyncPromoCoordinator.java
index 2e0a71fd..04f81d69 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupSyncPromoCoordinator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupSyncPromoCoordinator.java
@@ -4,43 +4,35 @@
 
 package org.chromium.chrome.browser.educational_tip.cards;
 
-import android.content.Context;
-
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 
 import org.chromium.base.CallbackController;
+import org.chromium.chrome.browser.educational_tip.EducationTipModuleActionDelegate;
 import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider;
 import org.chromium.chrome.browser.educational_tip.R;
 import org.chromium.chrome.browser.hub.PaneId;
 
 /** Coordinator for the Tab group sync promo card. */
 public class TabGroupSyncPromoCoordinator implements EducationalTipCardProvider {
-
-    private final Context mContext;
-    private final ShowHubPaneCallback mShowHubPaneRunnable;
+    private final EducationTipModuleActionDelegate mActionDelegate;
     private final Runnable mOnClickedRunnable;
-    private CallbackController mCallbackController;
 
     /**
-     * @param context The Context of the application.
      * @param onModuleClickedCallback The callback to be called when the module is clicked.
      * @param callbackController The instance of {@link CallbackController}.
-     * @param showHubPaneCallback The callback to open a Hub Pane.
+     * @param actionDelegate The instance of {@link EducationTipModuleActionDelegate}.
      */
     public TabGroupSyncPromoCoordinator(
-            @NonNull Context context,
             @NonNull Runnable onModuleClickedCallback,
             @NonNull CallbackController callbackController,
-            @NonNull ShowHubPaneCallback showHubPaneCallback) {
-        mContext = context;
-        mShowHubPaneRunnable = showHubPaneCallback;
-        mCallbackController = callbackController;
+            @NonNull EducationTipModuleActionDelegate actionDelegate) {
+        mActionDelegate = actionDelegate;
 
         mOnClickedRunnable =
-                mCallbackController.makeCancelable(
+                callbackController.makeCancelable(
                         () -> {
-                            mShowHubPaneRunnable.onClick(PaneId.TAB_GROUPS);
+                            mActionDelegate.openHubPane(PaneId.TAB_GROUPS);
                             onModuleClickedCallback.run();
                         });
     }
@@ -49,12 +41,16 @@
 
     @Override
     public String getCardTitle() {
-        return mContext.getString(R.string.educational_tip_tab_group_sync_title);
+        return mActionDelegate
+                .getContext()
+                .getString(R.string.educational_tip_tab_group_sync_title);
     }
 
     @Override
     public String getCardDescription() {
-        return mContext.getString(R.string.educational_tip_tab_group_sync_description);
+        return mActionDelegate
+                .getContext()
+                .getString(R.string.educational_tip_tab_group_sync_description);
     }
 
     @Override
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java
index b647ea10..051c8c3f 100644
--- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/DefaultBrowserPromoCoordinatorUnitTest.java
@@ -9,6 +9,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import androidx.test.filters.SmallTest;
 
@@ -17,7 +18,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.RuntimeEnvironment;
@@ -39,20 +39,20 @@
         shadows = {ShadowAppCompatResources.class})
 public class DefaultBrowserPromoCoordinatorUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
     @Mock private Runnable mOnModuleClickedCallback;
     @Mock private BottomSheetController mBottomSheetController;
+    @Mock private EducationTipModuleActionDelegate mActionDelegate;
 
     private DefaultBrowserPromoCoordinator mDefaultBrowserPromoCoordinator;
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
+        when(mActionDelegate.getContext()).thenReturn(RuntimeEnvironment.application);
+        when(mActionDelegate.getBottomSheetController()).thenReturn(mBottomSheetController);
 
         mDefaultBrowserPromoCoordinator =
-                new DefaultBrowserPromoCoordinator(
-                        RuntimeEnvironment.application,
-                        mOnModuleClickedCallback,
-                        mBottomSheetController);
+                new DefaultBrowserPromoCoordinator(mOnModuleClickedCallback, mActionDelegate);
     }
 
     @Test
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java
index dc27a5b..82ccd16 100644
--- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleBuilderUnitTest.java
@@ -10,8 +10,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
-import android.view.ViewGroup;
-
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -21,19 +19,15 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
-import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.Callback;
-import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
-import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.ShowHubPaneCallback;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate;
 import org.chromium.chrome.browser.magic_stack.ModuleProvider;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.ui.shadows.ShadowAppCompatResources;
 
 /** Test relating to {@link EducationalTipModuleBuilder} */
@@ -46,25 +40,13 @@
 
     @Mock private ModuleDelegate mModuleDelegate;
     @Mock private Callback<ModuleProvider> mBuildCallback;
-    @Mock private BottomSheetController mBottomSheetController;
-    @Mock private ViewGroup mParentView;
-    @Mock private ShowHubPaneCallback mShowHubPaneCallback;
+    @Mock private EducationTipModuleActionDelegate mActionDelegate;
 
     private EducationalTipModuleBuilder mModuleBuilder;
-    private ObservableSupplierImpl<ViewGroup> mParentViewSupplier;
 
     @Before
     public void setUp() {
-        mParentViewSupplier = new ObservableSupplierImpl<>();
-        mParentViewSupplier.set(mParentView);
-
-        mModuleBuilder =
-                new EducationalTipModuleBuilder(
-                        RuntimeEnvironment.application,
-                        mBottomSheetController,
-                        new ObservableSupplierImpl<>(),
-                        mShowHubPaneCallback,
-                        mParentViewSupplier);
+        mModuleBuilder = new EducationalTipModuleBuilder(mActionDelegate);
     }
 
     @Test
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/QuickDeletePromoCoordinatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/QuickDeletePromoCoordinatorUnitTest.java
new file mode 100644
index 0000000..4da1a3a
--- /dev/null
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/QuickDeletePromoCoordinatorUnitTest.java
@@ -0,0 +1,51 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.educational_tip;
+
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.CallbackController;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.educational_tip.cards.QuickDeletePromoCoordinator;
+import org.chromium.ui.shadows.ShadowAppCompatResources;
+
+/** Test relating to {@link QuickDeletePromoCoordinator} */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        shadows = {ShadowAppCompatResources.class})
+public class QuickDeletePromoCoordinatorUnitTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Mock private Runnable mOnModuleClickedCallback;
+    @Mock private EducationTipModuleActionDelegate mActionDelegate;
+
+    private QuickDeletePromoCoordinator mQuickDeletePromoCoordinator;
+
+    @Before
+    public void setUp() {
+        mQuickDeletePromoCoordinator =
+                new QuickDeletePromoCoordinator(
+                        mOnModuleClickedCallback, new CallbackController(), mActionDelegate);
+    }
+
+    @Test
+    @SmallTest
+    public void testClickTabGroupPromoCard() {
+        mQuickDeletePromoCoordinator.onCardClicked();
+        verify(mActionDelegate).openAndHighlightQuickDeleteMenuItem();
+        verify(mOnModuleClickedCallback).run();
+    }
+}
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/TabGroupPromoCoordinatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/TabGroupPromoCoordinatorUnitTest.java
index b28ea0b..4d3921b 100644
--- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/TabGroupPromoCoordinatorUnitTest.java
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/TabGroupPromoCoordinatorUnitTest.java
@@ -4,13 +4,7 @@
 
 package org.chromium.chrome.browser.educational_tip;
 
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.view.ViewGroup;
 
 import androidx.test.filters.SmallTest;
 
@@ -24,16 +18,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.CallbackController;
-import org.chromium.base.supplier.ObservableSupplierImpl;
-import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Features.EnableFeatures;
-import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.ShowHubPaneCallback;
 import org.chromium.chrome.browser.educational_tip.cards.TabGroupPromoCoordinator;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.hub.PaneId;
-import org.chromium.chrome.browser.tab_ui.TabGridIphDialogCoordinator;
-import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.shadows.ShadowAppCompatResources;
 
 /** Test relating to {@link TabGroupPromoCoordinator} */
@@ -43,55 +29,23 @@
         shadows = {ShadowAppCompatResources.class})
 public class TabGroupPromoCoordinatorUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
-    @Mock private Context mContext;
-    @Mock private ModalDialogManager mModalDialogManager;
     @Mock private Runnable mOnModuleClickedCallback;
-    @Mock private ShowHubPaneCallback mShowHubPaneCallback;
-    @Mock private Supplier<ViewGroup> mParentViewSupplier;
-    @Mock private ViewGroup mParentView;
-    @Mock private TabGridIphDialogCoordinator mTabGridIphDialogCoordinator;
-
-    private ObservableSupplierImpl<ModalDialogManager> mModalDialogManagerSupplier;
+    @Mock private EducationTipModuleActionDelegate mActionDelegate;
 
     private TabGroupPromoCoordinator mTabGroupPromoCoordinator;
-    private CallbackController mCallbackController;
 
     @Before
     public void setUp() {
-        mModalDialogManagerSupplier = new ObservableSupplierImpl<>();
-        mModalDialogManagerSupplier.set(mModalDialogManager);
-
-        when(mParentViewSupplier.get()).thenReturn(mParentView);
-        mCallbackController = new CallbackController();
-
         mTabGroupPromoCoordinator =
                 new TabGroupPromoCoordinator(
-                        mContext,
-                        mOnModuleClickedCallback,
-                        mCallbackController,
-                        mModalDialogManagerSupplier,
-                        mShowHubPaneCallback,
-                        mParentViewSupplier);
-        mTabGroupPromoCoordinator.setTabGridIphDialogCoordinatorForTesting(
-                mTabGridIphDialogCoordinator);
+                        mOnModuleClickedCallback, new CallbackController(), mActionDelegate);
     }
 
     @Test
     @SmallTest
-    @EnableFeatures({ChromeFeatureList.EDUCATIONAL_TIP_MODULE})
     public void testClickTabGroupPromoCard() {
-        assertTrue(ChromeFeatureList.sEducationalTipModule.isEnabled());
-
         mTabGroupPromoCoordinator.onCardClicked();
-        verify(mTabGridIphDialogCoordinator).showIph();
-        verify(mShowHubPaneCallback).onClick(eq(PaneId.TAB_SWITCHER));
+        verify(mActionDelegate).openTabGroupIphDialog();
         verify(mOnModuleClickedCallback).run();
     }
-
-    @Test
-    @SmallTest
-    public void testDestroy() {
-        mTabGroupPromoCoordinator.destroy();
-        verify(mTabGridIphDialogCoordinator).setParentView(eq(null));
-    }
 }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/TabGroupSyncPromoCoordinatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/TabGroupSyncPromoCoordinatorUnitTest.java
index 1b529f1..6cc1d24 100644
--- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/TabGroupSyncPromoCoordinatorUnitTest.java
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/TabGroupSyncPromoCoordinatorUnitTest.java
@@ -4,12 +4,9 @@
 
 package org.chromium.chrome.browser.educational_tip;
 
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 
-import android.content.Context;
-
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -23,10 +20,7 @@
 
 import org.chromium.base.CallbackController;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Features.EnableFeatures;
-import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider.ShowHubPaneCallback;
 import org.chromium.chrome.browser.educational_tip.cards.TabGroupSyncPromoCoordinator;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.hub.PaneId;
 import org.chromium.ui.shadows.ShadowAppCompatResources;
 
@@ -37,33 +31,23 @@
         shadows = {ShadowAppCompatResources.class})
 public class TabGroupSyncPromoCoordinatorUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
-    @Mock private Context mContext;
     @Mock private Runnable mOnModuleClickedCallback;
-    @Mock private ShowHubPaneCallback mShowHubPaneCallback;
+    @Mock private EducationTipModuleActionDelegate mActionDelegate;
 
     private TabGroupSyncPromoCoordinator mTabGroupSyncPromoCoordinator;
-    private CallbackController mCallbackController;
 
     @Before
     public void setUp() {
-        mCallbackController = new CallbackController();
-
         mTabGroupSyncPromoCoordinator =
                 new TabGroupSyncPromoCoordinator(
-                        mContext,
-                        mOnModuleClickedCallback,
-                        mCallbackController,
-                        mShowHubPaneCallback);
+                        mOnModuleClickedCallback, new CallbackController(), mActionDelegate);
     }
 
     @Test
     @SmallTest
-    @EnableFeatures({ChromeFeatureList.EDUCATIONAL_TIP_MODULE})
     public void testClickTabGroupPromoCard() {
-        assertTrue(ChromeFeatureList.sEducationalTipModule.isEnabled());
-
         mTabGroupSyncPromoCoordinator.onCardClicked();
-        verify(mShowHubPaneCallback).onClick(eq(PaneId.TAB_GROUPS));
+        verify(mActionDelegate).openHubPane(eq(PaneId.TAB_GROUPS));
         verify(mOnModuleClickedCallback).run();
     }
 }
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index caf4a30e..5ce8eb2 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -317,16 +317,6 @@
     "api/tab_capture/tab_capture_api.h",
     "api/tab_capture/tab_capture_registry.cc",
     "api/tab_capture/tab_capture_registry.h",
-    "api/tab_groups/tab_groups_api.cc",
-    "api/tab_groups/tab_groups_api.h",
-    "api/tab_groups/tab_groups_constants.cc",
-    "api/tab_groups/tab_groups_constants.h",
-    "api/tab_groups/tab_groups_event_router.cc",
-    "api/tab_groups/tab_groups_event_router.h",
-    "api/tab_groups/tab_groups_event_router_factory.cc",
-    "api/tab_groups/tab_groups_event_router_factory.h",
-    "api/tab_groups/tab_groups_util.cc",
-    "api/tab_groups/tab_groups_util.h",
     "api/tabs/app_base_window.cc",
     "api/tabs/app_base_window.h",
     "api/tabs/app_window_controller.cc",
@@ -1253,7 +1243,6 @@
       "//chrome/browser/ash/login/demo_mode",
       "//chrome/browser/ash/login/quick_unlock",
       "//chrome/browser/ash/login/session",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/net",
       "//chrome/browser/ash/ownership",
       "//chrome/browser/ash/platform_keys",
@@ -1270,6 +1259,7 @@
       "//chrome/browser/devtools",
       "//chrome/browser/nearby_sharing/common",
       "//chrome/browser/ui/ash/keyboard",
+      "//chrome/browser/ui/ash/login",
       "//chrome/browser/ui/ash/media_client",
       "//chrome/browser/ui/ash/shelf/app_service",
       "//chrome/browser/ui/ash/thumbnail_loader",
@@ -1363,13 +1353,13 @@
       "//chrome/browser/ash/login/demo_mode",
       "//chrome/browser/ash/login/quick_unlock",
       "//chrome/browser/ash/login/session",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/net",
       "//chrome/browser/ash/platform_keys",
       "//chrome/browser/ash/policy/core",
       "//chrome/browser/ash/policy/handlers",
       "//chrome/browser/ash/system_web_apps/apps",
       "//chrome/browser/ash/system_logs",
+      "//chrome/browser/ui/ash/login",
       "//chrome/browser/ui/ash/shelf/app_service",
       "//chrome/browser/ui/ash/thumbnail_loader",
       "//chrome/browser/ui/webui/ash/settings/pages/a11y",
diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc
index fb8973ab..79349a8 100644
--- a/chrome/browser/extensions/activity_log/activity_log.cc
+++ b/chrome/browser/extensions/activity_log/activity_log.cc
@@ -21,14 +21,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_checker.h"
 #include "base/values.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
 #include "chrome/browser/extensions/activity_log/counting_policy.h"
 #include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
@@ -354,8 +352,8 @@
   // the thread hops.
   // TODO(devlin): We should probably be doing this more extensively throughout
   // extensions code.
-  if (g_browser_process->IsShuttingDown() ||
-      !g_browser_process->profile_manager()->IsValidProfile(browser_context)) {
+  if (ExtensionsBrowserClient::Get()->IsShuttingDown() ||
+      !ExtensionsBrowserClient::Get()->IsValidContext(browser_context)) {
     return nullptr;
   }
   return ActivityLog::GetInstance(browser_context);
diff --git a/chrome/browser/extensions/api/BUILD.gn b/chrome/browser/extensions/api/BUILD.gn
index 61a2b28..d041f7e 100644
--- a/chrome/browser/extensions/api/BUILD.gn
+++ b/chrome/browser/extensions/api/BUILD.gn
@@ -44,6 +44,7 @@
     "//chrome/browser/extensions/api/proxy",
     "//chrome/browser/extensions/api/safe_browsing_private",
     "//chrome/browser/extensions/api/scripting",
+    "//chrome/browser/extensions/api/tab_groups",
     "//chrome/browser/extensions/api/webrtc_desktop_capture_private",
     "//chrome/browser/extensions/api/webrtc_logging_private",
   ]
diff --git a/chrome/browser/extensions/api/cookies/cookies_api.cc b/chrome/browser/extensions/api/cookies/cookies_api.cc
index e7ecb33..42b1863 100644
--- a/chrome/browser/extensions/api/cookies/cookies_api.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_api.cc
@@ -481,17 +481,34 @@
   if (!cookie_manager)
     return RespondNow(Error(std::move(error)));
 
-  if (!cookies_helpers::ValidateCrossSiteAncestor(
-          parsed_args_->details.url, parsed_args_->details.partition_key,
-          &error)) {
+  // cookies.set api allows for an partitionKey with a `top_level_site` present
+  // but no value for `has_cross_site_ancestor`. If that is the case, the
+  // browser will calculate the value for `has_cross_site_ancestor`.
+  std::optional<extensions::api::cookies::CookiePartitionKey> api_partition_key;
+  if (parsed_args_->details.partition_key.has_value()) {
+    api_partition_key = parsed_args_->details.partition_key->Clone();
+    if (!api_partition_key->has_cross_site_ancestor.has_value() &&
+        api_partition_key->top_level_site.has_value()) {
+      base::expected<bool, std::string> cross_site_ancestor =
+          cookies_helpers::CalculateHasCrossSiteAncestor(
+              parsed_args_->details.url, api_partition_key);
+      if (!cross_site_ancestor.has_value()) {
+        return RespondNow(Error(std::move(cross_site_ancestor.error())));
+      }
+      api_partition_key->has_cross_site_ancestor = cross_site_ancestor.value();
+    }
+  }
+
+  if (!cookies_helpers::ValidateCrossSiteAncestor(parsed_args_->details.url,
+                                                  api_partition_key, &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
   base::expected<std::optional<net::CookiePartitionKey>, std::string>
-      partition_key = cookies_helpers::ToNetCookiePartitionKey(
-          parsed_args_->details.partition_key);
-  if (!partition_key.has_value()) {
-    return RespondNow(Error(std::move(partition_key.error())));
+      net_partition_key =
+          cookies_helpers::ToNetCookiePartitionKey(api_partition_key);
+  if (!net_partition_key.has_value()) {
+    return RespondNow(Error(std::move(net_partition_key.error())));
   }
 
   if (!parsed_args_->details.store_id)
@@ -541,7 +558,7 @@
           parsed_args_->details.http_only.value_or(false),       //
           same_site,                                             //
           net::COOKIE_PRIORITY_DEFAULT,                          //
-          partition_key.value(),                                 //
+          net_partition_key.value(),                             //
           /*status=*/nullptr));
   if (!cc) {
     // Return error through callbacks so that the proper error message
@@ -566,7 +583,8 @@
       base::BindOnce(&CookiesSetFunction::SetCanonicalCookieCallback, this));
   cookies_helpers::GetCookieListFromManager(
       cookie_manager, url_,
-      net::CookiePartitionKeyCollection::FromOptional(partition_key.value()),
+      net::CookiePartitionKeyCollection::FromOptional(
+          net_partition_key.value()),
       base::BindOnce(&CookiesSetFunction::GetCookieListCallback, this));
 
   // Will finish asynchronously.
diff --git a/chrome/browser/extensions/api/cookies/cookies_helpers.cc b/chrome/browser/extensions/api/cookies/cookies_helpers.cc
index 5581ba47..5f6ee9ec 100644
--- a/chrome/browser/extensions/api/cookies/cookies_helpers.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_helpers.cc
@@ -284,13 +284,20 @@
   // value of false.
   if (partition_key->top_level_site.value().empty()) {
     if (partition_key->has_cross_site_ancestor.has_value() &&
-        !partition_key->has_cross_site_ancestor.value()) {
+        partition_key->has_cross_site_ancestor.value()) {
       *error_out = "CookiePartitionKey.hasCrossSiteAncestor is invalid.";
       return false;
     }
     return true;
   }
 
+  if (!partition_key->has_cross_site_ancestor.has_value()) {
+    *error_out =
+        "Can not validate an empty value for "
+        "hasCrossSiteAncestor.";
+    return false;
+  }
+
   GURL url = GURL(url_string);
   if (!url.is_valid()) {
     *error_out = "Invalid url_string.";
@@ -303,15 +310,14 @@
     return false;
   }
 
-  // has_cross_site_ancestor can not be true if url and top_level_site aren't
+  // has_cross_site_ancestor can not be false if url and top_level_site aren't
   // first party.
-  if (!net::SiteForCookies::FromUrl(GURL(url)).IsFirstParty(top_level_site)) {
-    if (!partition_key->has_cross_site_ancestor.value()) {
-      *error_out =
-          "partitionKey has a first party value for hasCrossSiteAncestor "
-          "when the url and the topLevelSite are not first party.";
-      return false;
-    }
+  if (!net::SiteForCookies::FromUrl(GURL(url)).IsFirstParty(top_level_site) &&
+      !partition_key->has_cross_site_ancestor.value()) {
+    *error_out =
+        "partitionKey has a first party value for hasCrossSiteAncestor "
+        "when the url and the topLevelSite are not first party.";
+    return false;
   }
   return true;
 }
diff --git a/chrome/browser/extensions/api/cookies/cookies_helpers_unittest.cc b/chrome/browser/extensions/api/cookies/cookies_helpers_unittest.cc
index 44a43cc..7027479d 100644
--- a/chrome/browser/extensions/api/cookies/cookies_helpers_unittest.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_helpers_unittest.cc
@@ -38,4 +38,68 @@
   EXPECT_EQ(std::numeric_limits<double>::max(), *expiration_time);
 }
 
+TEST(CookiesHelperUnittest, ValidateCrossSiteAncestorErrorCases) {
+  std::string site = "https://example.com";
+  std::string invalid_site = "invalid";
+  struct {
+    std::string description;
+    std::string url;
+    std::optional<std::string> top_level_site;
+    std::optional<bool> has_cross_site_ancestor;
+    std::string error_str;
+  } error_cases[] = {
+      {/*description=*/"No top_level_site and a has_cross_site_ancestor",
+       /*url=*/site, /*top_level_site=*/std::nullopt,
+       /*has_cross_site_ancestor=*/true,
+       /*error_str=*/
+       "CookiePartitionKey.topLevelSite is not present when "
+       "CookiePartitionKey.hasCrossSiteAncestor is present."},
+      {/*description=*/"Empty string for top_level_site and a "
+                       "has_cross_site_ancestor` value of true",
+       /*url=*/site, /*top_level_site=*/"", /*has_cross_site_ancestor=*/true,
+       /*error_str=*/"CookiePartitionKey.hasCrossSiteAncestor is invalid."},
+      {/*description=*/"Invalid url",
+       /*url=*/invalid_site, /*top_level_site=*/site,
+       /*has_cross_site_ancestor=*/true, /*error_str=*/"Invalid url_string."},
+      {/*description=*/"Invalid top_level_site",
+       /*url=*/site, /*top_level_site=*/invalid_site,
+       /*has_cross_site_ancestor=*/true,
+       /*error_str=*/"Invalid value for CookiePartitionKey.topLevelSite."},
+      {/*description=*/"has_cross_site_ancestor can not be true if url and "
+                       "top_level_site` aren't first party.",
+       /*url=*/site, /*top_level_site=*/invalid_site,
+       /*has_cross_site_ancestor=*/false,
+       /*error_str=*/"Invalid value for CookiePartitionKey.topLevelSite."},
+      {/*description=*/"has_cross_site_ancestor must be populated for "
+                       "validation.",
+       /*url=*/site, /*top_level_site=*/site,
+       /*has_cross_site_ancestor=*/std::nullopt,
+       /*error_str=*/
+       "Can not validate an empty value "
+       "for hasCrossSiteAncestor."},
+  };
+
+  for (const auto& tc : error_cases) {
+    SCOPED_TRACE(tc.description);
+    base::Value::Dict partition_key_vals;
+
+    if (tc.top_level_site.has_value()) {
+      partition_key_vals.Set("topLevelSite", *tc.top_level_site);
+    }
+    if (tc.has_cross_site_ancestor.has_value()) {
+      partition_key_vals.Set("hasCrossSiteAncestor",
+                             *tc.has_cross_site_ancestor);
+    }
+
+    auto partition_key =
+        extensions::api::cookies::CookiePartitionKey::FromValue(
+            partition_key_vals);
+    ASSERT_TRUE(partition_key.has_value());
+
+    std::string error;
+    EXPECT_FALSE(cookies_helpers::ValidateCrossSiteAncestor(
+        tc.url, partition_key, &error));
+    EXPECT_EQ(tc.error_str, error);
+  }
+}
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc
index 7f778573..6399fad 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -46,6 +46,7 @@
 #include "chrome/browser/download/download_shelf.h"
 #include "chrome/browser/download/download_stats.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
+#include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/icon_loader.h"
 #include "chrome/browser/icon_manager.h"
 #include "chrome/browser/platform_util.h"
@@ -1516,14 +1517,17 @@
             download_extension_errors::kOpenPermission, &error)) {
     return RespondNow(Error(std::move(error)));
   }
-  Browser* browser = ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
-  if (Fault(!browser, download_extension_errors::kInvisibleContext, &error))
-    return RespondNow(Error(std::move(error)));
-  content::WebContents* web_contents =
-      browser->tab_strip_model()->GetActiveWebContents();
-  if (Fault(!web_contents, download_extension_errors::kInvisibleContext,
-            &error))
-    return RespondNow(Error(std::move(error)));
+
+  WindowController* window_controller =
+      ChromeExtensionFunctionDetails(this).GetCurrentWindowController();
+  if (!window_controller) {
+    return RespondNow(Error(download_extension_errors::kInvisibleContext));
+  }
+  content::WebContents* active_contents = window_controller->GetActiveTab();
+  if (!active_contents) {
+    return RespondNow(Error(download_extension_errors::kInvisibleContext));
+  }
+
   // Extensions with debugger permission could fake user gestures and should
   // not be trusted.
   if (GetSenderWebContents() &&
@@ -1539,7 +1543,7 @@
   // to avoid showing the prompt.
   DownloadOpenPrompt* download_open_prompt =
       DownloadOpenPrompt::CreateDownloadOpenConfirmationDialog(
-          web_contents, extension()->name(), download_item->GetFullPath(),
+          active_contents, extension()->name(), download_item->GetFullPath(),
           base::BindOnce(&DownloadsOpenFunction::OpenPromptDone, this,
                          params->download_id));
   if (on_prompt_created_cb_)
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index 144fa2537..c8fed90d 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -692,8 +692,11 @@
     if (!browser)
       error = kNoActiveWindowFound;
   } else {
-    browser = ExtensionTabUtil::GetBrowserInProfileWithId(
-        profile, window_id, include_incognito_information(), &error);
+    if (WindowController* controller =
+            ExtensionTabUtil::GetControllerInProfileWithId(
+                profile, window_id, include_incognito_information(), &error)) {
+      browser = controller->GetBrowser();
+    }
   }
 
   if (!browser) {
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
index 7cf26e42..41149400 100644
--- a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
@@ -76,6 +76,13 @@
   web_flow_started_ = true;
 }
 
+void GaiaRemoteConsentFlow::Stop() {
+  if (web_flow_) {
+    web_flow_->Stop();
+  }
+  profile_ = nullptr;
+}
+
 void GaiaRemoteConsentFlow::ReactToConsentResult(
     const std::string& consent_result) {
   bool consent_approved = false;
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
index b25f8fc..bfeae9f 100644
--- a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
@@ -57,6 +57,7 @@
 
   // Starts the flow by setting accounts in cookie.
   void Start();
+  void Stop();
 
   // Handles `consent_result` value when using either a Browser Tab or an App
   // Window to display the Auth page.
@@ -78,7 +79,7 @@
   network::mojom::CookieManager* GetCookieManagerForPartition();
 
   const raw_ptr<Delegate> delegate_;
-  const raw_ptr<Profile> profile_;
+  raw_ptr<Profile> profile_;
   const RemoteConsentResolutionData resolution_data_;
   const bool user_gesture_;
 
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
index d3f5b8b..35949de8 100644
--- a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
@@ -9,6 +9,8 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/keep_alive_registry/keep_alive_types.h"
+#include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
@@ -125,18 +127,23 @@
     return primary_account_info;
   }
 
-  void LaunchAndWaitGaiaRemoteConsentFlow() {
+  void CreateGaiaRemoteConsentFlow(const GURL& url) {
     CoreAccountInfo account_info = CreateFakeAccountInfoAndSetAsPrimary();
     ExtensionTokenKey token_key("extension_id", account_info,
                                 std::set<std::string>());
     RemoteConsentResolutionData resolution_data;
-    resolution_data.url = fake_gaia_test_server()->GetURL("/title1.html");
+    resolution_data.url = url;
 
     flow_ = std::make_unique<GaiaRemoteConsentFlow>(&mock(), profile(),
                                                     token_key, resolution_data,
                                                     /*user_gesture=*/true);
+  }
 
-    content::TestNavigationObserver navigation_observer(resolution_data.url);
+  void LaunchAndWaitGaiaRemoteConsentFlow() {
+    GURL url = fake_gaia_test_server()->GetURL("/title1.html");
+    CreateGaiaRemoteConsentFlow(url);
+
+    content::TestNavigationObserver navigation_observer(url);
     navigation_observer.StartWatchingNewWebContents();
 
     flow_->Start();
@@ -209,5 +216,21 @@
   SimulateConsentResult(approved_consent);
 }
 
+IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest,
+                       SimulateProfileShutdownWhileLoading) {
+  // We want to interrupt the flow before `auth_url` gets loaded. To ensure that
+  // an URL doesn't load prematurely, use a default test URL that never returns
+  // a response.
+  CreateGaiaRemoteConsentFlow(fake_gaia_test_server()->GetURL("/hung"));
+  flow()->Start();
+  // Delegate shouldn't be called after the profile is destroyed.
+  EXPECT_CALL(mock(), OnGaiaRemoteConsentFlowFailed).Times(0);
+  auto keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::BROWSER, KeepAliveRestartOption::DISABLED);
+  CloseBrowserSynchronously(browser());
+  base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(
+      FROM_HERE, std::move(keep_alive));
+}
+
 }  // namespace extensions
 #endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 7475fadc..966f6b08 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -64,6 +64,8 @@
 #include "components/guest_view/browser/guest_view_manager_delegate.h"
 #include "components/guest_view/browser/guest_view_manager_factory.h"
 #include "components/guest_view/browser/test_guest_view_manager.h"
+#include "components/keep_alive_registry/keep_alive_types.h"
+#include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_reconcilor.h"
 #include "components/signin/public/base/consent_level.h"
@@ -90,6 +92,7 @@
 #include "extensions/common/manifest_handlers/oauth2_manifest_handler.h"
 #include "google_apis/gaia/oauth2_mint_token_flow.h"
 #include "net/cookies/cookie_util.h"
+#include "net/test/embedded_test_server/default_handlers.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
@@ -922,6 +925,11 @@
   }
 
   void TearDownOnMainThread() override {
+    if (!identity_test_env_profile_adaptor_) {
+      // In some tests, we have released the profile early and removed the
+      // observer, so do nothing
+      return;
+    }
     identity_test_env()->identity_manager()->RemoveDiagnosticsObserver(this);
     IdentityTestWithSignin::TearDownOnMainThread();
   }
@@ -1913,6 +1921,35 @@
 }
 #endif  // !BUILDFLAG(IS_MAC)
 
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
+                       InteractiveSigninFailedDuringProfileShutDown) {
+  SignIn("primary@example.com");
+  auto keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::BROWSER, KeepAliveRestartOption::DISABLED);
+  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
+  func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
+  func->push_mint_token_result(TestOAuth2MintTokenFlow::REMOTE_CONSENT_SUCCESS);
+  // Have GetAuthTokenFunction make the request for the access token to ensure
+  // that the function doesn't immediately succeed.
+  func->set_auto_login_access_token(false);
+
+  RunFunctionAsync(func.get(), "[{\"interactive\": true}]");
+  identity_test_env()->identity_manager()->RemoveDiagnosticsObserver(this);
+  identity_test_env_profile_adaptor_.reset();
+  CloseBrowserSynchronously(browser());
+  EXPECT_FALSE(func->scope_ui_shown());
+
+  // The login screen should not be shown when the profile is shutting
+  // down.
+  EXPECT_FALSE(func->login_ui_shown());
+  EXPECT_EQ(std::string(errors::kBrowserContextShutDown),
+            WaitForError(func.get()));
+  base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(
+      FROM_HERE, std::move(keep_alive));
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) {
   SignIn("primary@example.com");
   scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
@@ -2016,14 +2053,15 @@
 
   // After the request is canceled, the function will complete.
   func->OnIdentityAPIShutdown();
-  EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get()));
+  EXPECT_EQ(std::string(errors::kBrowserContextShutDown),
+            WaitForError(func.get()));
   EXPECT_FALSE(func->login_ui_shown());
   EXPECT_FALSE(func->scope_ui_shown());
 
   QueueRequestComplete(type, &queued_request);
   histogram_tester()->ExpectUniqueSample(
       kGetAuthTokenResultHistogramName,
-      IdentityGetAuthTokenError::State::kCanceled, 1);
+      IdentityGetAuthTokenError::State::kBrowserContextShutDown, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveShutdown) {
@@ -2037,10 +2075,11 @@
 
   // After the request is canceled, the function will complete.
   func->OnIdentityAPIShutdown();
-  EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get()));
+  EXPECT_EQ(std::string(errors::kBrowserContextShutDown),
+            WaitForError(func.get()));
   histogram_tester()->ExpectUniqueSample(
       kGetAuthTokenResultHistogramName,
-      IdentityGetAuthTokenError::State::kCanceled, 1);
+      IdentityGetAuthTokenError::State::kBrowserContextShutDown, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
@@ -2294,10 +2333,11 @@
   RunFunctionAsync(func.get(), "[{}]");
 
   id_api()->Shutdown();
-  EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get()));
+  EXPECT_EQ(std::string(errors::kBrowserContextShutDown),
+            WaitForError(func.get()));
   histogram_tester()->ExpectUniqueSample(
       kGetAuthTokenResultHistogramName,
-      IdentityGetAuthTokenError::State::kCanceled, 1);
+      IdentityGetAuthTokenError::State::kBrowserContextShutDown, 1);
 }
 
 // Ensure that when there are multiple active function calls, IdentityAPI
@@ -2333,9 +2373,9 @@
   // Shut down IdentityAPI and ensure that both functions complete with an
   // error.
   id_api()->Shutdown();
-  EXPECT_EQ(std::string(errors::kCanceled),
+  EXPECT_EQ(std::string(errors::kBrowserContextShutDown),
             func1_runner.WaitForError(func1.get()));
-  EXPECT_EQ(std::string(errors::kCanceled),
+  EXPECT_EQ(std::string(errors::kBrowserContextShutDown),
             func2_runner.WaitForError(func2.get()));
 }
 
@@ -3802,6 +3842,32 @@
       IdentityLaunchWebAuthFlowFunction::Error::kUserRejected, 1);
 }
 
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, ProfileShutDown) {
+  std::unique_ptr<net::EmbeddedTestServer> https_server =
+      std::make_unique<net::EmbeddedTestServer>(
+          net::EmbeddedTestServer::TYPE_HTTPS);
+  net::test_server::RegisterDefaultHandlers(https_server.get());
+  EXPECT_TRUE(https_server->Start());
+  // We want to interrupt the flow before `auth_url` gets loaded. To ensure that
+  // an URL doesn't load prematurely, use a default test URL that never returns
+  // a response.
+  GURL auth_url(https_server->GetURL("/hung"));
+  auto keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::BROWSER, KeepAliveRestartOption::DISABLED);
+  scoped_refptr<IdentityLaunchWebAuthFlowFunction> function =
+      CreateLaunchWebAuthFlowFunction();
+  std::string args =
+      "[{\"interactive\": true, \"url\": \"" + auth_url.spec() + "\"}]";
+  RunFunctionAsync(function.get(), args);
+  CloseBrowserSynchronously(browser());
+  EXPECT_EQ(std::string(errors::kBrowserContextShutDown),
+            WaitForError(function.get()));
+  base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(
+      FROM_HERE, std::move(keep_alive));
+}
+#endif
+
 // Regression test for http://b/290733700.
 IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
                        SchemeOtherThanHttpOrHttpsNotAllowed) {
diff --git a/chrome/browser/extensions/api/identity/identity_constants.cc b/chrome/browser/extensions/api/identity/identity_constants.cc
index ec84ec0..a15e875 100644
--- a/chrome/browser/extensions/api/identity/identity_constants.cc
+++ b/chrome/browser/extensions/api/identity/identity_constants.cc
@@ -26,12 +26,12 @@
 const char kPageLoadFailure[] = "Authorization page could not be loaded.";
 const char kPageLoadTimedOut[] = "Authorization page load timed out.";
 const char kInvalidConsentResult[] = "Returned an invalid consent result.";
-const char kCanceled[] = "canceled";
 const char kCannotCreateWindow[] =
     "Couldn't create a browser window to display an authorization page.";
 const char kInvalidURLScheme[] =
     "The auth url has an invalid scheme. Only http:// and https:// schemes are "
     "allowed.";
+const char kBrowserContextShutDown[] = "The browser context has been shut down";
 
 const int kCachedRemoteConsentTTLSeconds = 1;
 }  // namespace identity_constants
diff --git a/chrome/browser/extensions/api/identity/identity_constants.h b/chrome/browser/extensions/api/identity/identity_constants.h
index e8aa983..af6878d 100644
--- a/chrome/browser/extensions/api/identity/identity_constants.h
+++ b/chrome/browser/extensions/api/identity/identity_constants.h
@@ -23,9 +23,9 @@
 extern const char kPageLoadFailure[];
 extern const char kPageLoadTimedOut[];
 extern const char kInvalidConsentResult[];
-extern const char kCanceled[];
 extern const char kCannotCreateWindow[];
 extern const char kInvalidURLScheme[];
+extern const char kBrowserContextShutDown[];
 
 extern const int kCachedRemoteConsentTTLSeconds;
 }  // namespace identity_constants
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
index 2d12fb3..44ccaee 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
@@ -67,12 +67,12 @@
       return identity_constants::kPageLoadFailure;
     case State::kInvalidConsentResult:
       return identity_constants::kInvalidConsentResult;
-    case State::kCanceled:
-      return identity_constants::kCanceled;
     case State::kInteractivityDenied:
       return identity_constants::kGetAuthTokenInteractivityDeniedError;
     case State::kCannotCreateWindow:
       return identity_constants::kCannotCreateWindow;
+    case State::kBrowserContextShutDown:
+      return identity_constants::kBrowserContextShutDown;
   }
 }
 
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h b/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
index 4c9492c..b84c306 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
@@ -42,10 +42,11 @@
     kRemoteConsentPageLoadFailure = 24,
     // kSetAccountsInCookieFailure = 25, // Deprecated
     kInvalidConsentResult = 26,
-    kCanceled = 27,
+    // kCanceled = 27, // Deprecated
     kInteractivityDenied = 28,
     kCannotCreateWindow = 29,
-    kMaxValue = kCannotCreateWindow,
+    kBrowserContextShutDown = 30,
+    kMaxValue = kBrowserContextShutDown,
   };
 
   // Constructs a |State::kMintTokenAuthFailure| error with an
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
index ef837fc..b57fd80c 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
@@ -18,7 +18,6 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/identity/identity_api.h"
 #include "chrome/browser/extensions/api/identity/identity_get_auth_token_error.h"
 #include "chrome/browser/profiles/profile.h"
@@ -38,6 +37,7 @@
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/common/api/oauth2.h"
 #include "extensions/common/manifest_handlers/oauth2_manifest_handler.h"
 #include "google_apis/gaia/gaia_auth_util.h"
@@ -47,6 +47,7 @@
 #include "ui/base/idle/idle.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
 #include "chromeos/components/kiosk/kiosk_utils.h"
 #include "chromeos/components/mgs/managed_guest_session_utils.h"
@@ -311,7 +312,7 @@
   // In kiosk mode, interactive sign-in is not supported.
   SigninFailed();
 #else
-  if (g_browser_process->IsShuttingDown()) {
+  if (ExtensionsBrowserClient::Get()->IsShuttingDown()) {
     // The login prompt cannot be displayed when the browser process is shutting
     // down.
     SigninFailed();
@@ -755,6 +756,9 @@
 #if BUILDFLAG(IS_CHROMEOS)
   device_oauth2_token_fetcher_.reset();
 #endif
+  if (gaia_remote_consent_flow_) {
+    gaia_remote_consent_flow_->Stop();
+  }
   token_key_account_access_token_fetcher_.reset();
   scoped_identity_manager_observation_.Reset();
   extensions::IdentityAPI::GetFactoryInstance()
@@ -762,8 +766,8 @@
       ->mint_queue()
       ->RequestCancel(token_key_, this);
 
-  CompleteFunctionWithError(
-      IdentityGetAuthTokenError(IdentityGetAuthTokenError::State::kCanceled));
+  CompleteFunctionWithError(IdentityGetAuthTokenError(
+      IdentityGetAuthTokenError::State::kBrowserContextShutDown));
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc b/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc
index 3d73906..e779b6b 100644
--- a/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc
@@ -41,6 +41,7 @@
       return IdentityLaunchWebAuthFlowFunction::Error::kPageLoadTimedOut;
     case WebAuthFlow::CANNOT_CREATE_WINDOW:
       return IdentityLaunchWebAuthFlowFunction::Error::kCannotCreateWindow;
+
     default:
       NOTREACHED_IN_MIGRATION()
           << "Unexpected error from web auth flow: " << failure;
@@ -70,6 +71,8 @@
       return identity_constants::kCannotCreateWindow;
     case IdentityLaunchWebAuthFlowFunction::Error::kInvalidURLScheme:
       return identity_constants::kInvalidURLScheme;
+    case IdentityLaunchWebAuthFlowFunction::Error::kBrowserContextShutDown:
+      return identity_constants::kBrowserContextShutDown;
   }
 }
 
@@ -186,6 +189,15 @@
   return true;
 }
 
+void IdentityLaunchWebAuthFlowFunction::OnBrowserContextShutdown() {
+  if (auth_flow_) {
+    auth_flow_->Stop();
+  }
+  RecordHistogramFunctionResult(Error::kBrowserContextShutDown);
+  CompleteAsyncRun(
+      ExtensionFunction::Error(ErrorToString(Error::kBrowserContextShutDown)));
+}
+
 void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLDomainsForTest(
     const std::string& extension_id) {
   InitFinalRedirectURLDomains(extension_id, nullptr);
@@ -214,10 +226,7 @@
   Error error = WebAuthFlowFailureToError(failure);
 
   RecordHistogramFunctionResult(error);
-  RespondWithError(ErrorToString(error));
-  if (auth_flow_)
-    auth_flow_.release()->DetachDelegateAndDelete();
-  Release();  // Balanced in Run.
+  CompleteAsyncRun(ExtensionFunction::Error(ErrorToString(error)));
 }
 
 void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange(
@@ -227,11 +236,16 @@
   }
   RecordHistogramFunctionResult(
       IdentityLaunchWebAuthFlowFunction::Error::kNone);
-  Respond(WithArguments(redirect_url.spec()));
+  CompleteAsyncRun(WithArguments(redirect_url.spec()));
+}
+
+void IdentityLaunchWebAuthFlowFunction::CompleteAsyncRun(
+    ResponseValue response) {
+  Respond(std::move(response));
   if (auth_flow_) {
     auth_flow_.release()->DetachDelegateAndDelete();
   }
-  Release();  // Balanced in RunAsync.
+  Release();  // Balanced in Run.
 }
 
 WebAuthFlow* IdentityLaunchWebAuthFlowFunction::GetWebAuthFlowForTesting() {
diff --git a/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h b/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h
index 79bc79b..d3444d4 100644
--- a/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h
+++ b/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h
@@ -40,7 +40,8 @@
     kPageLoadTimedOut = 6,
     kCannotCreateWindow = 7,
     kInvalidURLScheme = 8,
-    kMaxValue = kInvalidURLScheme,
+    kBrowserContextShutDown = 9,
+    kMaxValue = kBrowserContextShutDown,
   };
 
   IdentityLaunchWebAuthFlowFunction();
@@ -58,7 +59,8 @@
   ~IdentityLaunchWebAuthFlowFunction() override;
   ResponseAction Run() override;
   bool ShouldKeepWorkerAliveIndefinitely() override;
-
+  void OnBrowserContextShutdown() override;
+  void CompleteAsyncRun(ResponseValue response);
   void StartAuthFlow(Profile* profile,
                      GURL auth_url,
                      WebAuthFlow::Mode mode,
diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.cc b/chrome/browser/extensions/api/identity/web_auth_flow.cc
index 78fa383..05cd9c6b 100644
--- a/chrome/browser/extensions/api/identity/web_auth_flow.cc
+++ b/chrome/browser/extensions/api/identity/web_auth_flow.cc
@@ -104,6 +104,16 @@
                                                                 this);
 }
 
+void WebAuthFlow::Stop() {
+  if (web_contents()) {
+    web_contents()->Close();
+  }
+  WebContentsObserver::Observe(nullptr);
+  web_contents_.reset();
+  delegate_ = nullptr;
+  profile_ = nullptr;
+}
+
 void WebAuthFlow::DisplayInfoBar() {
   DCHECK(web_contents());
 
diff --git a/chrome/browser/extensions/api/identity/web_auth_flow.h b/chrome/browser/extensions/api/identity/web_auth_flow.h
index 9c28dc2b..4af7799 100644
--- a/chrome/browser/extensions/api/identity/web_auth_flow.h
+++ b/chrome/browser/extensions/api/identity/web_auth_flow.h
@@ -56,7 +56,7 @@
     INTERACTION_REQUIRED,  // Non-redirect page load in silent mode.
     LOAD_FAILED,
     TIMED_OUT,
-    CANNOT_CREATE_WINDOW  // Couldn't create a browser window.
+    CANNOT_CREATE_WINDOW,  // Couldn't create a browser window.
   };
 
   enum class AbortOnLoad {
@@ -114,6 +114,10 @@
   // Prevents further calls to the delegate and deletes the flow.
   void DetachDelegateAndDelete();
 
+  // Immediately closes the webview and prevents further delegate calls. Can be
+  // called before `DetachDelegateAndDelete()` to release resources immediately.
+  void Stop();
+
   // This call will make the interactive mode, that opens up a browser tab for
   // auth, display an Infobar that shows the extension name.
   void SetShouldShowInfoBar(const std::string& extension_display_name);
@@ -145,7 +149,7 @@
   void CloseInfoBar();
 
   raw_ptr<Delegate> delegate_ = nullptr;
-  const raw_ptr<Profile> profile_;
+  raw_ptr<Profile> profile_;
   const GURL provider_url_;
   const Mode mode_;
   const bool user_gesture_;
diff --git a/chrome/browser/extensions/api/sessions/sessions_api.cc b/chrome/browser/extensions/api/sessions/sessions_api.cc
index 9baf139..3f886c6 100644
--- a/chrome/browser/extensions/api/sessions/sessions_api.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_api.cc
@@ -20,7 +20,6 @@
 #include "base/time/time.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/api/sessions/session_id.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/api/tabs/windows_util.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
@@ -40,6 +39,7 @@
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
 #include "components/sync_sessions/session_sync_service.h"
 #include "components/sync_sessions/synced_session.h"
+#include "components/tab_groups/tab_group_color.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_function_dispatcher.h"
@@ -183,8 +183,8 @@
     const sessions::tab_restore::Group& group) {
   DCHECK(!group.tabs.empty());
 
-  return tab_groups_util::CreateTabGroupObject(group.group_id,
-                                               group.visual_data);
+  return ExtensionTabUtil::CreateTabGroupObject(group.group_id,
+                                                group.visual_data);
 }
 
 api::sessions::Session SessionsGetRecentlyClosedFunction::CreateSessionModel(
@@ -446,16 +446,15 @@
 
 ExtensionFunction::ResponseValue
 SessionsRestoreFunction::GetRestoredWindowResult(int window_id) {
-  Browser* browser = nullptr;
+  WindowController* window_controller = nullptr;
   std::string error;
-  if (!windows_util::GetBrowserFromWindowID(this, window_id, 0, &browser,
-                                            &error)) {
+  if (!windows_util::GetControllerFromWindowID(this, window_id, 0,
+                                               &window_controller, &error)) {
     return Error(error);
   }
   base::Value::Dict window_value =
-      ExtensionTabUtil::CreateWindowValueForExtension(
-          *browser, extension(), WindowController::kPopulateTabs,
-          source_context_type());
+      window_controller->CreateWindowValueForExtension(
+          extension(), WindowController::kPopulateTabs, source_context_type());
   std::optional<api::windows::Window> window =
       api::windows::Window::FromValue(window_value);
   DCHECK(window);
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 eb87bf2..c44bbe1 100644
--- a/chrome/browser/extensions/api/side_panel/side_panel_service.cc
+++ b/chrome/browser/extensions/api/side_panel/side_panel_service.cc
@@ -223,10 +223,11 @@
     int window_id,
     bool include_incognito_information) {
   std::string error;
-  Browser* browser = ExtensionTabUtil::GetBrowserInProfileWithId(
-      Profile::FromBrowserContext(context), window_id,
-      include_incognito_information, &error);
-  if (!browser) {
+  WindowController* window_controller =
+      ExtensionTabUtil::GetControllerInProfileWithId(
+          Profile::FromBrowserContext(context), window_id,
+          include_incognito_information, &error);
+  if (!window_controller) {
     return base::unexpected(error);
   }
 
@@ -238,7 +239,8 @@
   }
 
   side_panel_util::OpenGlobalExtensionSidePanel(
-      *browser, /*web_contents=*/nullptr, extension.id());
+      *window_controller->GetBrowser(), /*web_contents=*/nullptr,
+      extension.id());
   return true;
 }
 
diff --git a/chrome/browser/extensions/api/tab_groups/BUILD.gn b/chrome/browser/extensions/api/tab_groups/BUILD.gn
new file mode 100644
index 0000000..05d1d4b
--- /dev/null
+++ b/chrome/browser/extensions/api/tab_groups/BUILD.gn
@@ -0,0 +1,39 @@
+# 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("//extensions/buildflags/buildflags.gni")
+
+assert(enable_extensions)
+
+source_set("tab_groups") {
+  sources = [
+    "tab_groups_api.cc",
+    "tab_groups_api.h",
+    "tab_groups_event_router.cc",
+    "tab_groups_event_router.h",
+    "tab_groups_event_router_factory.cc",
+    "tab_groups_event_router_factory.h",
+  ]
+
+  configs += [ "//build/config/compiler:wexit_time_destructors" ]
+
+  public_deps = [
+    "//base",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui",
+    "//chrome/browser/ui/tabs:tab_strip_model_observer",
+    "//chrome/common/extensions/api",
+    "//components/tab_groups",
+    "//extensions/browser",
+  ]
+
+  deps = [
+    "//chrome/browser/extensions",
+    "//chrome/browser/ui:ui_features",
+    "//chrome/common/extensions",
+    "//content/public/browser",
+    "//extensions/common",
+    "//ui/gfx/range",
+  ]
+}
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc
index ce3ef15..c61dd92 100644
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc
+++ b/chrome/browser/extensions/api/tab_groups/tab_groups_api.cc
@@ -11,8 +11,6 @@
 
 #include "base/strings/pattern.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_constants.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/api/tabs/windows_util.h"
 #include "chrome/browser/extensions/browser_extension_window_controller.h"
@@ -41,6 +39,11 @@
 
 namespace {
 
+constexpr char kCannotMoveGroupIntoMiddleOfOtherGroupError[] =
+    "Cannot move the group to an index that is in the middle of another group.";
+constexpr char kCannotMoveGroupIntoMiddleOfPinnedTabsError[] =
+    "Cannot move the group to an index that is in the middle of pinned tabs.";
+
 // Returns true if a group could be moved into the |target_index| of the given
 // |tab_strip|. Sets the |error| string otherwise.
 bool IndexSupportsGroupMove(TabStripModel* tab_strip,
@@ -52,7 +55,7 @@
   }
 
   if (tab_strip->IsTabPinned(target_index)) {
-    *error = tab_groups_constants::kCannotMoveGroupIntoMiddleOfPinnedTabsError;
+    *error = kCannotMoveGroupIntoMiddleOfPinnedTabsError;
     return false;
   }
 
@@ -62,7 +65,7 @@
       tab_strip->GetTabGroupForTab(target_index - 1);
 
   if (target_group.has_value() && target_group == adjacent_group) {
-    *error = tab_groups_constants::kCannotMoveGroupIntoMiddleOfOtherGroupError;
+    *error = kCannotMoveGroupIntoMiddleOfOtherGroupError;
     return false;
   }
 
@@ -80,16 +83,16 @@
   tab_groups::TabGroupId id = tab_groups::TabGroupId::CreateEmpty();
   const tab_groups::TabGroupVisualData* visual_data = nullptr;
   std::string error;
-  if (!tab_groups_util::GetGroupById(group_id, browser_context(),
-                                     include_incognito_information(), nullptr,
-                                     &id, &visual_data, &error)) {
+  if (!ExtensionTabUtil::GetGroupById(group_id, browser_context(),
+                                      include_incognito_information(), nullptr,
+                                      &id, &visual_data, &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
   DCHECK(!id.is_empty());
 
   return RespondNow(ArgumentList(api::tab_groups::Get::Results::Create(
-      tab_groups_util::CreateTabGroupObject(id, *visual_data))));
+      ExtensionTabUtil::CreateTabGroupObject(id, *visual_data))));
 }
 
 ExtensionFunction::ResponseAction TabGroupsQueryFunction::Run() {
@@ -99,8 +102,14 @@
 
   base::Value::List result_list;
   Profile* profile = Profile::FromBrowserContext(browser_context());
-  Browser* current_browser =
-      ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
+
+  WindowController* window_controller =
+      ChromeExtensionFunctionDetails(this).GetCurrentWindowController();
+  if (!window_controller) {
+    return RespondNow(Error(tabs_constants::kNoCurrentWindowError));
+  }
+  Browser* current_browser = window_controller->GetBrowser();
+
   for (Browser* browser : *BrowserList::GetInstance()) {
     if (!profile->IsSameOrParent(browser->profile()))
       continue;
@@ -150,12 +159,12 @@
 
       if (params->query_info.color != api::tab_groups::Color::kNone &&
           params->query_info.color !=
-              tab_groups_util::ColorIdToColor(visual_data->color())) {
+              ExtensionTabUtil::ColorIdToColor(visual_data->color())) {
         continue;
       }
 
       result_list.Append(
-          tab_groups_util::CreateTabGroupObject(id, *visual_data).ToValue());
+          ExtensionTabUtil::CreateTabGroupObject(id, *visual_data).ToValue());
     }
   }
 
@@ -172,9 +181,9 @@
   tab_groups::TabGroupId id = tab_groups::TabGroupId::CreateEmpty();
   const tab_groups::TabGroupVisualData* visual_data = nullptr;
   std::string error;
-  if (!tab_groups_util::GetGroupById(group_id, browser_context(),
-                                     include_incognito_information(), &browser,
-                                     &id, &visual_data, &error)) {
+  if (!ExtensionTabUtil::GetGroupById(group_id, browser_context(),
+                                      include_incognito_information(), &browser,
+                                      &id, &visual_data, &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
@@ -186,7 +195,7 @@
 
   tab_groups::TabGroupColorId color = visual_data->color();
   if (params->update_properties.color != api::tab_groups::Color::kNone) {
-    color = tab_groups_util::ColorToColorId(params->update_properties.color);
+    color = ExtensionTabUtil::ColorToColorId(params->update_properties.color);
   }
 
   std::u16string title = visual_data->title();
@@ -209,8 +218,8 @@
     return RespondNow(NoArguments());
 
   return RespondNow(ArgumentList(api::tab_groups::Get::Results::Create(
-      tab_groups_util::CreateTabGroupObject(tab_group->id(),
-                                            *tab_group->visual_data()))));
+      ExtensionTabUtil::CreateTabGroupObject(tab_group->id(),
+                                             *tab_group->visual_data()))));
 }
 
 ExtensionFunction::ResponseAction TabGroupsMoveFunction::Run() {
@@ -234,7 +243,7 @@
     return RespondNow(NoArguments());
 
   return RespondNow(ArgumentList(api::tab_groups::Get::Results::Create(
-      *tab_groups_util::CreateTabGroupObject(group))));
+      *ExtensionTabUtil::CreateTabGroupObject(group))));
 }
 
 bool TabGroupsMoveFunction::MoveGroup(int group_id,
@@ -244,7 +253,7 @@
                                       std::string* error) {
   Browser* source_browser = nullptr;
   const tab_groups::TabGroupVisualData* visual_data = nullptr;
-  if (!tab_groups_util::GetGroupById(
+  if (!ExtensionTabUtil::GetGroupById(
           group_id, browser_context(), include_incognito_information(),
           &source_browser, group, &visual_data, error)) {
     return false;
@@ -269,13 +278,13 @@
   }
 
   if (window_id) {
-    Browser* target_browser = nullptr;
-
-    if (!windows_util::GetBrowserFromWindowID(
+    WindowController* window_controller = nullptr;
+    if (!windows_util::GetControllerFromWindowID(
             this, *window_id, WindowController::GetAllWindowFilter(),
-            &target_browser, error)) {
+            &window_controller, error)) {
       return false;
     }
+    Browser* target_browser = window_controller->GetBrowser();
 
     // TODO(crbug.com/40638654): Rather than calling is_type_normal(), should
     // this call SupportsWindowFeature(Browser::FEATURE_TABSTRIP)?
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_api_unittest.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_api_unittest.cc
index cd1a4a4..f9537fe 100644
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_api_unittest.cc
+++ b/chrome/browser/extensions/api/tab_groups/tab_groups_api_unittest.cc
@@ -16,10 +16,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_constants.h"
 #include "chrome/browser/extensions/api/tab_groups/tab_groups_event_router.h"
 #include "chrome/browser/extensions/api/tab_groups/tab_groups_event_router_factory.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
@@ -46,7 +44,6 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/test_event_router_observer.h"
 #include "extensions/common/constants.h"
-#include "extensions/common/error_utils.h"
 #include "extensions/common/extension_builder.h"
 
 namespace extensions {
@@ -243,7 +240,7 @@
 
   const base::Value& group_info = groups_list[0];
   ASSERT_TRUE(group_info.is_dict());
-  EXPECT_EQ(tab_groups_util::GetGroupId(group1),
+  EXPECT_EQ(ExtensionTabUtil::GetGroupId(group1),
             *group_info.GetDict().FindInt("id"));
 }
 
@@ -280,7 +277,7 @@
 
   const base::Value& group_info = groups_list[0];
   ASSERT_EQ(base::Value::Type::DICT, group_info.type());
-  EXPECT_EQ(tab_groups_util::GetGroupId(group3),
+  EXPECT_EQ(ExtensionTabUtil::GetGroupId(group3),
             *group_info.GetDict().FindInt("id"));
 }
 
@@ -298,7 +295,7 @@
   tab_groups::TabGroupVisualData visual_data(
       u"Title", tab_groups::TabGroupColorId::kBlue);
   tab_group_model->GetTabGroup(group)->SetVisualData(visual_data);
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Use the TabGroupsGetFunction to get the group object.
   constexpr char kFormatArgs[] = R"([%d])";
@@ -320,9 +317,7 @@
   function->set_extension(extension);
   std::string error = api_test_utils::RunFunctionAndReturnError(
       function.get(), "[0]", profile(), api_test_utils::FunctionMode::kNone);
-  EXPECT_EQ(ErrorUtils::FormatErrorMessage(
-                tab_groups_constants::kGroupNotFoundError, "0"),
-            error);
+  EXPECT_EQ("No group with id: 0.", error);
 }
 
 // Test that updating group metadata works as expected.
@@ -339,7 +334,7 @@
   tab_groups::TabGroupVisualData visual_data(
       u"Initial title", tab_groups::TabGroupColorId::kBlue);
   tab_group_model->GetTabGroup(group)->SetVisualData(visual_data);
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Use the TabGroupsUpdateFunction to update the title and color.
   auto function = base::MakeRefCounted<TabGroupsUpdateFunction>();
@@ -369,9 +364,7 @@
   std::string error = api_test_utils::RunFunctionAndReturnError(
       function.get(), "[0, {}]", profile(),
       api_test_utils::FunctionMode::kNone);
-  EXPECT_EQ(ErrorUtils::FormatErrorMessage(
-                tab_groups_constants::kGroupNotFoundError, "0"),
-            error);
+  EXPECT_EQ("No group with id: 0.", error);
 }
 
 // Test that tabGroups.update() passes on a saved group.
@@ -394,7 +387,7 @@
   saved_service->SetIsInitializedForTesting(true);
   saved_service->AddGroup(
       tab_groups::SavedTabGroupUtils::CreateSavedTabGroupFromLocalId(group));
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
   ASSERT_TRUE(saved_service->GetGroup(group));
 
   scoped_refptr<const Extension> extension = CreateTabGroupsExtension();
@@ -425,7 +418,7 @@
 
   // Create a group with multiple tabs.
   tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup({1, 2, 3});
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Use the TabGroupsMoveFunction to move the group to index 2.
   auto function = base::MakeRefCounted<TabGroupsMoveFunction>();
@@ -460,7 +453,7 @@
   tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup({1, 2, 3});
   tab_groups::TabGroupId group_2 = tab_strip_model->AddToNewGroup({4, 5});
 
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Use the TabGroupsMoveFunction to move the group to index 3.
   auto function = base::MakeRefCounted<TabGroupsMoveFunction>();
@@ -498,7 +491,7 @@
   tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup({1, 2});
   tab_groups::TabGroupId group_2 = tab_strip_model->AddToNewGroup({4, 5});
 
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Use the TabGroupsMoveFunction to move the group to index 3.
   auto function = base::MakeRefCounted<TabGroupsMoveFunction>();
@@ -531,7 +524,7 @@
 
   // Create a group with multiple tabs.
   tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup({2, 3, 4});
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Use the TabGroupsMoveFunction to move the group to index 0.
   auto function = base::MakeRefCounted<TabGroupsMoveFunction>();
@@ -563,7 +556,7 @@
 
   // Create a group with multiple tabs.
   tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup({2, 3, 4});
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Create a new window and add a few tabs.
   auto window2 = std::make_unique<TestBrowserWindow>();
@@ -632,7 +625,7 @@
 
   // Create a group with an unpinned tab.
   tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup({4});
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Try to move the group to index 1 and expect an error.
   auto function = base::MakeRefCounted<TabGroupsMoveFunction>();
@@ -641,8 +634,9 @@
   const std::string args = base::StringPrintf(kFormatArgs, group_id);
   std::string error = api_test_utils::RunFunctionAndReturnError(
       function.get(), args, profile(), api_test_utils::FunctionMode::kNone);
-  EXPECT_EQ(tab_groups_constants::kCannotMoveGroupIntoMiddleOfPinnedTabsError,
-            error);
+  EXPECT_EQ(
+      "Cannot move the group to an index that is in the middle of pinned tabs.",
+      error);
 }
 
 // Test that a group cannot be moved into the middle of another group.
@@ -656,7 +650,7 @@
   // Create two tab groups, one with multiple tabs and the other to move.
   tab_strip_model->AddToNewGroup({0, 1, 2});
   tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup({4});
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Try to move the second group to index 1 and expect an error.
   auto function = base::MakeRefCounted<TabGroupsMoveFunction>();
@@ -665,8 +659,10 @@
   const std::string args = base::StringPrintf(kFormatArgs, group_id);
   std::string error = api_test_utils::RunFunctionAndReturnError(
       function.get(), args, profile(), api_test_utils::FunctionMode::kNone);
-  EXPECT_EQ(tab_groups_constants::kCannotMoveGroupIntoMiddleOfOtherGroupError,
-            error);
+  EXPECT_EQ(
+      "Cannot move the group to an index that is in the middle of another "
+      "group.",
+      error);
 }
 
 TEST_F(TabGroupsApiUnitTest, TabGroupsOnCreated) {
@@ -736,7 +732,7 @@
   ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
 
   scoped_refptr<const Extension> extension = CreateTabGroupsExtension();
-  int group_id = tab_groups_util::GetGroupId(
+  int group_id = ExtensionTabUtil::GetGroupId(
       browser()->tab_strip_model()->AddToNewGroup({0}));
   const std::string args =
       base::StringPrintf(R"([%d, {"index": %d}])", group_id, 1);
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_constants.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_constants.cc
deleted file mode 100644
index 7e44b45f..0000000
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_constants.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_constants.h"
-
-namespace extensions {
-namespace tab_groups_constants {
-
-const char kCannotMoveGroupIntoMiddleOfOtherGroupError[] =
-    "Cannot move the group to an index that is in the middle of another group.";
-const char kCannotMoveGroupIntoMiddleOfPinnedTabsError[] =
-    "Cannot move the group to an index that is in the middle of pinned tabs.";
-const char kGroupNotFoundError[] = "No group with id: *.";
-
-}  // namespace tab_groups_constants
-}  // namespace extensions
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_constants.h b/chrome/browser/extensions/api/tab_groups/tab_groups_constants.h
deleted file mode 100644
index 3f7f45af..0000000
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_constants.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_TAB_GROUPS_TAB_GROUPS_CONSTANTS_H_
-#define CHROME_BROWSER_EXTENSIONS_API_TAB_GROUPS_TAB_GROUPS_CONSTANTS_H_
-
-namespace extensions {
-
-// Constants used for the Tab Groups API.
-namespace tab_groups_constants {
-
-// Error messages.
-extern const char kCannotMoveGroupIntoMiddleOfOtherGroupError[];
-extern const char kCannotMoveGroupIntoMiddleOfPinnedTabsError[];
-extern const char kGroupNotFoundError[];
-
-}  // namespace tab_groups_constants
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_API_TAB_GROUPS_TAB_GROUPS_CONSTANTS_H_
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_event_router.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_event_router.cc
index 2d22b57..5305a96 100644
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_event_router.cc
+++ b/chrome/browser/extensions/api/tab_groups/tab_groups_event_router.cc
@@ -8,7 +8,6 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -62,7 +61,7 @@
 
 void TabGroupsEventRouter::DispatchGroupCreated(tab_groups::TabGroupId group) {
   auto args(api::tab_groups::OnCreated::Create(
-      *tab_groups_util::CreateTabGroupObject(group)));
+      *ExtensionTabUtil::CreateTabGroupObject(group)));
 
   DispatchEvent(events::TAB_GROUPS_ON_CREATED,
                 api::tab_groups::OnCreated::kEventName, std::move(args));
@@ -70,7 +69,7 @@
 
 void TabGroupsEventRouter::DispatchGroupRemoved(tab_groups::TabGroupId group) {
   auto args(api::tab_groups::OnRemoved::Create(
-      *tab_groups_util::CreateTabGroupObject(group)));
+      *ExtensionTabUtil::CreateTabGroupObject(group)));
 
   DispatchEvent(events::TAB_GROUPS_ON_REMOVED,
                 api::tab_groups::OnRemoved::kEventName, std::move(args));
@@ -78,7 +77,7 @@
 
 void TabGroupsEventRouter::DispatchGroupMoved(tab_groups::TabGroupId group) {
   auto args(api::tab_groups::OnMoved::Create(
-      *tab_groups_util::CreateTabGroupObject(group)));
+      *ExtensionTabUtil::CreateTabGroupObject(group)));
 
   DispatchEvent(events::TAB_GROUPS_ON_MOVED,
                 api::tab_groups::OnMoved::kEventName, std::move(args));
@@ -86,7 +85,7 @@
 
 void TabGroupsEventRouter::DispatchGroupUpdated(tab_groups::TabGroupId group) {
   auto args(api::tab_groups::OnUpdated::Create(
-      *tab_groups_util::CreateTabGroupObject(group)));
+      *ExtensionTabUtil::CreateTabGroupObject(group)));
 
   DispatchEvent(events::TAB_GROUPS_ON_UPDATED,
                 api::tab_groups::OnUpdated::kEventName, std::move(args));
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_util.cc b/chrome/browser/extensions/api/tab_groups/tab_groups_util.cc
deleted file mode 100644
index a5825ac..0000000
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_util.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
-
-#include "base/hash/hash.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/token.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_constants.h"
-#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/tabs/tab_group.h"
-#include "chrome/browser/ui/tabs/tab_group_model.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/ui_features.h"
-#include "components/tab_groups/tab_group_color.h"
-#include "components/tab_groups/tab_group_id.h"
-#include "components/tab_groups/tab_group_visual_data.h"
-#include "content/public/browser/browser_context.h"
-#include "extensions/common/error_utils.h"
-
-namespace extensions {
-namespace tab_groups_util {
-
-int GetGroupId(const tab_groups::TabGroupId& id) {
-  uint32_t hash = base::PersistentHash(id.ToString());
-  return std::abs(static_cast<int>(hash));
-}
-
-int GetWindowIdOfGroup(const tab_groups::TabGroupId& id) {
-  Browser* browser = chrome::FindBrowserWithGroup(id, nullptr);
-  if (browser)
-    return browser->session_id().id();
-  return -1;
-}
-
-api::tab_groups::TabGroup CreateTabGroupObject(
-    const tab_groups::TabGroupId& id,
-    const tab_groups::TabGroupVisualData& visual_data) {
-  api::tab_groups::TabGroup tab_group_object;
-  tab_group_object.id = GetGroupId(id);
-  tab_group_object.collapsed = visual_data.is_collapsed();
-  tab_group_object.color = ColorIdToColor(visual_data.color());
-  tab_group_object.title = base::UTF16ToUTF8(visual_data.title());
-  tab_group_object.window_id = GetWindowIdOfGroup(id);
-
-  return tab_group_object;
-}
-
-std::optional<api::tab_groups::TabGroup> CreateTabGroupObject(
-    const tab_groups::TabGroupId& id) {
-  Browser* browser = chrome::FindBrowserWithGroup(id, nullptr);
-  if (!browser)
-    return std::nullopt;
-
-  CHECK(browser->tab_strip_model()->SupportsTabGroups());
-  TabGroupModel* group_model = browser->tab_strip_model()->group_model();
-  const tab_groups::TabGroupVisualData* visual_data =
-      group_model->GetTabGroup(id)->visual_data();
-
-  DCHECK(visual_data);
-
-  return CreateTabGroupObject(id, *visual_data);
-}
-
-bool GetGroupById(int group_id,
-                  content::BrowserContext* browser_context,
-                  bool include_incognito,
-                  Browser** browser,
-                  tab_groups::TabGroupId* id,
-                  const tab_groups::TabGroupVisualData** visual_data,
-                  std::string* error) {
-  if (group_id == -1)
-    return false;
-
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-  Profile* incognito_profile =
-      include_incognito && profile->HasPrimaryOTRProfile()
-          ? profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
-          : nullptr;
-  for (Browser* target_browser : *BrowserList::GetInstance()) {
-    if (target_browser->profile() == profile ||
-        target_browser->profile() == incognito_profile) {
-      TabStripModel* target_tab_strip = target_browser->tab_strip_model();
-      if (!target_tab_strip->SupportsTabGroups())
-        continue;
-      for (tab_groups::TabGroupId target_group :
-           target_tab_strip->group_model()->ListTabGroups()) {
-        if (GetGroupId(target_group) == group_id) {
-          if (browser)
-            *browser = target_browser;
-          if (id)
-            *id = target_group;
-          if (visual_data) {
-            *visual_data = target_tab_strip->group_model()
-                               ->GetTabGroup(target_group)
-                               ->visual_data();
-          }
-          return true;
-        }
-      }
-    }
-  }
-
-  *error =
-      ErrorUtils::FormatErrorMessage(tab_groups_constants::kGroupNotFoundError,
-                                     base::NumberToString(group_id));
-  return false;
-}
-
-bool GetGroupById(int group_id,
-                  content::BrowserContext* browser_context,
-                  bool include_incognito,
-                  tab_groups::TabGroupId* id,
-                  std::string* error) {
-  return GetGroupById(group_id, browser_context, include_incognito, nullptr, id,
-                      nullptr, error);
-}
-
-api::tab_groups::Color ColorIdToColor(
-    const tab_groups::TabGroupColorId& color_id) {
-  switch (color_id) {
-    case tab_groups::TabGroupColorId::kGrey:
-      return api::tab_groups::Color::kGrey;
-    case tab_groups::TabGroupColorId::kBlue:
-      return api::tab_groups::Color::kBlue;
-    case tab_groups::TabGroupColorId::kRed:
-      return api::tab_groups::Color::kRed;
-    case tab_groups::TabGroupColorId::kYellow:
-      return api::tab_groups::Color::kYellow;
-    case tab_groups::TabGroupColorId::kGreen:
-      return api::tab_groups::Color::kGreen;
-    case tab_groups::TabGroupColorId::kPink:
-      return api::tab_groups::Color::kPink;
-    case tab_groups::TabGroupColorId::kPurple:
-      return api::tab_groups::Color::kPurple;
-    case tab_groups::TabGroupColorId::kCyan:
-      return api::tab_groups::Color::kCyan;
-    case tab_groups::TabGroupColorId::kOrange:
-      return api::tab_groups::Color::kOrange;
-    case tab_groups::TabGroupColorId::kNumEntries:
-      NOTREACHED_IN_MIGRATION() << "kNumEntries is not a support color enum.";
-      return api::tab_groups::Color::kGrey;
-  }
-
-  NOTREACHED_IN_MIGRATION();
-  return api::tab_groups::Color::kCyan;
-}
-
-tab_groups::TabGroupColorId ColorToColorId(api::tab_groups::Color color) {
-  switch (color) {
-    case api::tab_groups::Color::kGrey:
-      return tab_groups::TabGroupColorId::kGrey;
-    case api::tab_groups::Color::kBlue:
-      return tab_groups::TabGroupColorId::kBlue;
-    case api::tab_groups::Color::kRed:
-      return tab_groups::TabGroupColorId::kRed;
-    case api::tab_groups::Color::kYellow:
-      return tab_groups::TabGroupColorId::kYellow;
-    case api::tab_groups::Color::kGreen:
-      return tab_groups::TabGroupColorId::kGreen;
-    case api::tab_groups::Color::kPink:
-      return tab_groups::TabGroupColorId::kPink;
-    case api::tab_groups::Color::kPurple:
-      return tab_groups::TabGroupColorId::kPurple;
-    case api::tab_groups::Color::kCyan:
-      return tab_groups::TabGroupColorId::kCyan;
-    case api::tab_groups::Color::kOrange:
-      return tab_groups::TabGroupColorId::kOrange;
-    case api::tab_groups::Color::kNone:
-      NOTREACHED_IN_MIGRATION();
-  }
-
-  NOTREACHED_IN_MIGRATION();
-  return tab_groups::TabGroupColorId::kGrey;
-}
-
-}  // namespace tab_groups_util
-}  // namespace extensions
diff --git a/chrome/browser/extensions/api/tab_groups/tab_groups_util.h b/chrome/browser/extensions/api/tab_groups/tab_groups_util.h
deleted file mode 100644
index eee3a68f..0000000
--- a/chrome/browser/extensions/api/tab_groups/tab_groups_util.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_API_TAB_GROUPS_TAB_GROUPS_UTIL_H_
-#define CHROME_BROWSER_EXTENSIONS_API_TAB_GROUPS_TAB_GROUPS_UTIL_H_
-
-#include <optional>
-#include <string>
-
-#include "chrome/common/extensions/api/tab_groups.h"
-#include "components/tab_groups/tab_group_color.h"
-
-class Browser;
-
-namespace content {
-class BrowserContext;
-}
-
-namespace tab_groups {
-class TabGroupId;
-class TabGroupVisualData;
-}  // namespace tab_groups
-
-namespace extensions {
-
-// Provides various utility functions that help manipulate tab groups.
-namespace tab_groups_util {
-
-// Gets the extensions-specific Group ID.
-int GetGroupId(const tab_groups::TabGroupId& id);
-
-// Gets the window ID that the group belongs to.
-int GetWindowIdOfGroup(const tab_groups::TabGroupId& id);
-
-// Creates a TabGroup object
-// (see chrome/common/extensions/api/tab_groups.json) with information about
-// the state of a tab group for the given group |id|. Most group metadata is
-// derived from the |visual_data|, which specifies group color, title, etc.
-api::tab_groups::TabGroup CreateTabGroupObject(
-    const tab_groups::TabGroupId& id,
-    const tab_groups::TabGroupVisualData& visual_data);
-std::optional<api::tab_groups::TabGroup> CreateTabGroupObject(
-    const tab_groups::TabGroupId& id);
-
-// Gets the metadata for the group with ID |group_id|. Sets the |error| if not
-// found. |browser|, |id|, or |visual_data| may be nullptr and will not be set
-// within the function if so.
-bool GetGroupById(int group_id,
-                  content::BrowserContext* browser_context,
-                  bool include_incognito,
-                  Browser** browser,
-                  tab_groups::TabGroupId* id,
-                  const tab_groups::TabGroupVisualData** visual_data,
-                  std::string* error);
-bool GetGroupById(int group_id,
-                  content::BrowserContext* browser_context,
-                  bool include_incognito,
-                  tab_groups::TabGroupId* id,
-                  std::string* error);
-
-// Conversions between the api::tab_groups::Color enum and the TabGroupColorId
-// enum.
-api::tab_groups::Color ColorIdToColor(
-    const tab_groups::TabGroupColorId& color_id);
-tab_groups::TabGroupColorId ColorToColorId(api::tab_groups::Color color);
-
-}  // namespace tab_groups_util
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_API_TAB_GROUPS_TAB_GROUPS_UTIL_H_
diff --git a/chrome/browser/extensions/api/tabs/app_window_controller.cc b/chrome/browser/extensions/api/tabs/app_window_controller.cc
index e670814..cc200959 100644
--- a/chrome/browser/extensions/api/tabs/app_window_controller.cc
+++ b/chrome/browser/extensions/api/tabs/app_window_controller.cc
@@ -54,21 +54,22 @@
   return nullptr;
 }
 
-bool AppWindowController::GetActiveTab(content::WebContents** contents,
-                                       int* optional_tab_id) const {
-  DCHECK(contents);
-
-  *contents = app_window_->web_contents();
-  if (*contents) {
-    if (optional_tab_id) {
-      *optional_tab_id = -1;
-    }
-    return true;
-  }
-
+bool AppWindowController::IsDeleteScheduled() const {
+  // App windows don't have the complicated multiphase tear-down.
   return false;
 }
 
+content::WebContents* AppWindowController::GetActiveTab() const {
+  return app_window_->web_contents();
+}
+
+bool AppWindowController::HasEditableTabStrip() const {
+  // There is no visible tab strip in app windows so we return true to
+  // indicate our tab strip animations are not blocking global
+  // operations (see WindowController::HasEditableTabStrip definition).
+  return true;
+}
+
 bool AppWindowController::IsVisibleToTabsAPIForExtension(
     const Extension* extension,
     bool allow_dev_tools_windows) const {
diff --git a/chrome/browser/extensions/api/tabs/app_window_controller.h b/chrome/browser/extensions/api/tabs/app_window_controller.h
index d301fe1..ebde1fb 100644
--- a/chrome/browser/extensions/api/tabs/app_window_controller.h
+++ b/chrome/browser/extensions/api/tabs/app_window_controller.h
@@ -37,8 +37,9 @@
                          const GURL& extension_url) const override;
   bool CanClose(Reason* reason) const override;
   Browser* GetBrowser() const override;
-  bool GetActiveTab(content::WebContents** contents,
-                    int* tab_id) const override;
+  bool IsDeleteScheduled() const override;
+  content::WebContents* GetActiveTab() const override;
+  bool HasEditableTabStrip() const override;
   bool IsVisibleToTabsAPIForExtension(
       const Extension* extension,
       bool allow_dev_tools_windows) const override;
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 1419f219..759c305 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -41,8 +41,6 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/devtools/devtools_window.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_constants.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/api/tabs/windows_util.h"
 #include "chrome/browser/extensions/browser_extension_window_controller.h"
@@ -181,27 +179,6 @@
   raw_ref<T> params_;
 };
 
-bool GetBrowserFromWindowID(const ChromeExtensionFunctionDetails& details,
-                            int window_id,
-                            Browser** browser,
-                            std::string* error) {
-  Browser* result = nullptr;
-  result = ExtensionTabUtil::GetBrowserFromWindowID(details, window_id, error);
-  if (!result)
-    return false;
-
-  *browser = result;
-  return true;
-}
-
-bool GetBrowserFromWindowID(ExtensionFunction* function,
-                            int window_id,
-                            Browser** browser,
-                            std::string* error) {
-  return GetBrowserFromWindowID(ChromeExtensionFunctionDetails(function),
-                                window_id, browser, error);
-}
-
 // |error_message| can optionally be passed in and will be set with an
 // appropriate message if the tab cannot be found by id.
 bool GetTabById(int tab_id,
@@ -240,13 +217,15 @@
                nullptr /* ignore TabStripModel* output */, &web_contents,
                nullptr /* ignore int tab_index output */, error);
   } else {
-    Browser* browser =
-        ChromeExtensionFunctionDetails(function).GetCurrentBrowser();
-    if (!browser) {
+    WindowController* window_controller =
+        ChromeExtensionFunctionDetails(function).GetCurrentWindowController();
+    if (!window_controller) {
       *error = tabs_constants::kNoCurrentWindowError;
-    } else if (!ExtensionTabUtil::GetActiveTab(browser, &web_contents,
-                                               nullptr)) {
-      *error = tabs_constants::kNoSelectedTabError;
+    } else {
+      web_contents = window_controller->GetActiveTab();
+      if (!web_contents) {
+        *error = tabs_constants::kNoSelectedTabError;
+      }
     }
   }
   return web_contents;
@@ -488,19 +467,19 @@
   EXTENSION_FUNCTION_VALIDATE(params);
 
   ApiParameterExtractor<windows::Get::Params> extractor(params);
-  Browser* browser = nullptr;
+  WindowController* window_controller = nullptr;
   std::string error;
-  if (!windows_util::GetBrowserFromWindowID(this, params->window_id,
-                                            extractor.type_filters(), &browser,
-                                            &error)) {
+  if (!windows_util::GetControllerFromWindowID(this, params->window_id,
+                                               extractor.type_filters(),
+                                               &window_controller, &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
   WindowController::PopulateTabBehavior populate_tab_behavior =
       extractor.populate_tabs() ? WindowController::kPopulateTabs
                                 : WindowController::kDontPopulateTabs;
-  base::Value::Dict windows = ExtensionTabUtil::CreateWindowValueForExtension(
-      *browser, extension(), populate_tab_behavior, source_context_type());
+  base::Value::Dict windows = window_controller->CreateWindowValueForExtension(
+      extension(), populate_tab_behavior, source_context_type());
   return RespondNow(WithArguments(std::move(windows)));
 }
 
@@ -510,19 +489,19 @@
   EXTENSION_FUNCTION_VALIDATE(params);
 
   ApiParameterExtractor<windows::GetCurrent::Params> extractor(params);
-  Browser* browser = nullptr;
+  WindowController* window_controller = nullptr;
   std::string error;
-  if (!windows_util::GetBrowserFromWindowID(
+  if (!windows_util::GetControllerFromWindowID(
           this, extension_misc::kCurrentWindowId, extractor.type_filters(),
-          &browser, &error)) {
+          &window_controller, &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
   WindowController::PopulateTabBehavior populate_tab_behavior =
       extractor.populate_tabs() ? WindowController::kPopulateTabs
                                 : WindowController::kDontPopulateTabs;
-  base::Value::Dict windows = ExtensionTabUtil::CreateWindowValueForExtension(
-      *browser, extension(), populate_tab_behavior, source_context_type());
+  base::Value::Dict windows = window_controller->CreateWindowValueForExtension(
+      extension(), populate_tab_behavior, source_context_type());
   return RespondNow(WithArguments(std::move(windows)));
 }
 
@@ -926,14 +905,16 @@
       windows::Update::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  Browser* browser = nullptr;
+  WindowController* window_controller = nullptr;
   std::string error;
-  if (!windows_util::GetBrowserFromWindowID(
+  if (!windows_util::GetControllerFromWindowID(
           this, params->window_id, WindowController::GetAllWindowFilter(),
-          &browser, &error)) {
+          &window_controller, &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
+  Browser* browser = window_controller->GetBrowser();
+
   // Don't allow locked fullscreen operations on a window without the proper
   // permission (also don't allow any operations on a locked window if the
   // extension doesn't have the permission).
@@ -1055,8 +1036,8 @@
     browser->window()->FlashFrame(*params->update_info.draw_attention);
 
   return RespondNow(
-      WithArguments(ExtensionTabUtil::CreateWindowValueForExtension(
-          *browser, extension(), WindowController::kDontPopulateTabs,
+      WithArguments(window_controller->CreateWindowValueForExtension(
+          extension(), WindowController::kDontPopulateTabs,
           source_context_type())));
 }
 
@@ -1065,28 +1046,28 @@
       windows::Remove::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  Browser* browser = nullptr;
+  WindowController* window_controller = nullptr;
   std::string error;
-  if (!windows_util::GetBrowserFromWindowID(this, params->window_id,
-                                            WindowController::kNoWindowFilter,
-                                            &browser, &error)) {
+  if (!windows_util::GetControllerFromWindowID(
+          this, params->window_id, WindowController::kNoWindowFilter,
+          &window_controller, &error)) {
     return RespondNow(Error(std::move(error)));
   }
 
-  if (platform_util::IsBrowserLockedFullscreen(browser) &&
+  if (platform_util::IsBrowserLockedFullscreen(
+          window_controller->GetBrowser()) &&
       !ExtensionHasLockedFullscreenPermission(extension())) {
     return RespondNow(
         Error(tabs_constants::kMissingLockWindowFullscreenPrivatePermission));
   }
 
-  WindowController* controller = browser->extension_window_controller();
   WindowController::Reason reason;
-  if (!controller->CanClose(&reason)) {
+  if (!window_controller->CanClose(&reason)) {
     return RespondNow(Error(reason == WindowController::REASON_NOT_EDITABLE
                                 ? tabs_constants::kTabStripNotEditableError
                                 : kUnknownErrorDoNotUse));
   }
-  controller->window()->Close();
+  window_controller->window()->Close();
   return RespondNow(NoArguments());
 }
 
@@ -1102,13 +1083,16 @@
   if (params->window_id)
     window_id = *params->window_id;
 
-  Browser* browser = nullptr;
   std::string error;
-  if (!GetBrowserFromWindowID(this, window_id, &browser, &error))
+  WindowController* window_controller =
+      ExtensionTabUtil::GetControllerFromWindowID(
+          ChromeExtensionFunctionDetails(this), window_id, &error);
+  if (!window_controller) {
     return RespondNow(Error(std::move(error)));
+  }
 
-  TabStripModel* tab_strip =
-      ExtensionTabUtil::GetEditableTabStripModel(browser);
+  TabStripModel* tab_strip = ExtensionTabUtil::GetEditableTabStripModel(
+      window_controller->GetBrowser());
   if (!tab_strip)
     return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
   WebContents* contents = tab_strip->GetActiveWebContents();
@@ -1128,13 +1112,16 @@
   if (params->window_id)
     window_id = *params->window_id;
 
-  Browser* browser = nullptr;
   std::string error;
-  if (!GetBrowserFromWindowID(this, window_id, &browser, &error))
+  WindowController* window_controller =
+      ExtensionTabUtil::GetControllerFromWindowID(
+          ChromeExtensionFunctionDetails(this), window_id, &error);
+  if (!window_controller) {
     return RespondNow(Error(std::move(error)));
+  }
 
-  return RespondNow(WithArguments(ExtensionTabUtil::CreateTabList(
-      browser, extension(), source_context_type())));
+  return RespondNow(WithArguments(
+      window_controller->CreateTabList(extension(), source_context_type())));
 }
 
 ExtensionFunction::ResponseAction TabsQueryFunction::Run() {
@@ -1184,8 +1171,14 @@
   Profile* profile = Profile::FromBrowserContext(browser_context());
   Browser* last_active_browser =
       chrome::FindAnyBrowser(profile, include_incognito_information());
-  Browser* current_browser =
-      ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
+
+  WindowController* window_controller =
+      ChromeExtensionFunctionDetails(this).GetCurrentWindowController();
+  if (!window_controller) {
+    return RespondNow(Error(tabs_constants::kNoCurrentWindowError));
+  }
+  Browser* current_browser = window_controller->GetBrowser();
+
   for (Browser* browser : *BrowserList::GetInstance()) {
     if (!profile->IsSameOrParent(browser->profile()))
       continue;
@@ -1258,7 +1251,7 @@
             continue;
         } else if (!group.has_value()) {
           continue;
-        } else if (tab_groups_util::GetGroupId(group.value()) !=
+        } else if (ExtensionTabUtil::GetGroupId(group.value()) !=
                    group_id.value()) {
           continue;
         }
@@ -1452,15 +1445,20 @@
   int window_id = params->highlight_info.window_id.value_or(
       extension_misc::kCurrentWindowId);
 
-  Browser* browser = nullptr;
   std::string error;
-  if (!GetBrowserFromWindowID(this, window_id, &browser, &error))
+  WindowController* window_controller =
+      ExtensionTabUtil::GetControllerFromWindowID(
+          ChromeExtensionFunctionDetails(this), window_id, &error);
+  if (!window_controller) {
     return RespondNow(Error(std::move(error)));
+  }
 
   // Don't let the extension update the tab if the user is dragging tabs.
-  TabStripModel* tabstrip = ExtensionTabUtil::GetEditableTabStripModel(browser);
-  if (!tabstrip)
+  TabStripModel* tab_strip_model = ExtensionTabUtil::GetEditableTabStripModel(
+      window_controller->GetBrowser());
+  if (!tab_strip_model) {
     return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
+  }
   ui::ListSelectionModel selection;
   std::optional<size_t> active_index;
 
@@ -1468,14 +1466,14 @@
     std::vector<int>& tab_indices = *params->highlight_info.tabs.as_integers;
     // Create a new selection model as we read the list of tab indices.
     for (int tab_index : tab_indices) {
-      if (!HighlightTab(tabstrip, &selection, &active_index, tab_index,
+      if (!HighlightTab(tab_strip_model, &selection, &active_index, tab_index,
                         &error)) {
         return RespondNow(Error(std::move(error)));
       }
     }
   } else {
     EXTENSION_FUNCTION_VALIDATE(params->highlight_info.tabs.as_integer);
-    if (!HighlightTab(tabstrip, &selection, &active_index,
+    if (!HighlightTab(tab_strip_model, &selection, &active_index,
                       *params->highlight_info.tabs.as_integer, &error)) {
       return RespondNow(Error(std::move(error)));
     }
@@ -1486,14 +1484,10 @@
     return RespondNow(Error(tabs_constants::kNoHighlightedTabError));
 
   selection.set_active(active_index);
-  TabStripModel* tab_strip_model =
-      ExtensionTabUtil::GetEditableTabStripModel(browser);
-  if (!tab_strip_model)
-    return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
   tab_strip_model->SetSelectionFromModel(std::move(selection));
   return RespondNow(
-      WithArguments(ExtensionTabUtil::CreateWindowValueForExtension(
-          *browser, extension(), WindowController::kPopulateTabs,
+      WithArguments(window_controller->CreateWindowValueForExtension(
+          extension(), WindowController::kPopulateTabs,
           source_context_type())));
 }
 
@@ -1527,17 +1521,19 @@
   int tab_id = -1;
   WebContents* contents = nullptr;
   if (!params->tab_id) {
-    Browser* browser = ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
-    if (!browser)
+    WindowController* window_controller =
+        ChromeExtensionFunctionDetails(this).GetCurrentWindowController();
+    if (!window_controller) {
       return RespondNow(Error(tabs_constants::kNoCurrentWindowError));
-    TabStripModel* tab_strip_model =
-        ExtensionTabUtil::GetEditableTabStripModel(browser);
-    if (!tab_strip_model)
+    }
+    if (!ExtensionTabUtil::IsTabStripEditable()) {
       return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
-    contents = tab_strip_model->GetActiveWebContents();
-    if (!contents)
+    }
+    contents = window_controller->GetActiveTab();
+    if (!contents) {
       return RespondNow(Error(tabs_constants::kNoSelectedTabError));
-    tab_id = sessions::SessionTabHelper::IdForTab(contents).id();
+    }
+    tab_id = ExtensionTabUtil::GetTabId(contents);
   } else {
     tab_id = *params->tab_id;
   }
@@ -1799,10 +1795,14 @@
   }
 
   if (window_id && *window_id != ExtensionTabUtil::GetWindowIdOfTab(contents)) {
-    Browser* target_browser = nullptr;
-    if (!GetBrowserFromWindowID(this, *window_id, &target_browser, error))
+    WindowController* target_controller =
+        ExtensionTabUtil::GetControllerFromWindowID(
+            ChromeExtensionFunctionDetails(this), *window_id, error);
+    if (!target_controller) {
       return false;
+    }
 
+    Browser* target_browser = target_controller->GetBrowser();
     int inserted_index =
         MoveTabToWindow(this, tab_id, target_browser, *new_index, error);
     if (inserted_index < 0)
@@ -1865,25 +1865,21 @@
 
   // If |tab_id| is specified, look for it. Otherwise default to selected tab
   // in the current window.
-
-  Browser* browser = nullptr;
   content::WebContents* web_contents = nullptr;
   if (!params->tab_id) {
-    Browser* current_browser =
-        ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
-
-    if (!current_browser)
+    if (WindowController* window_controller =
+            ChromeExtensionFunctionDetails(this).GetCurrentWindowController()) {
+      web_contents = window_controller->GetActiveTab();
+      if (!web_contents) {
+        return RespondNow(Error(tabs_constants::kNoSelectedTabError));
+      }
+    } else {
       return RespondNow(Error(tabs_constants::kNoCurrentWindowError));
-
-    if (!ExtensionTabUtil::GetActiveTab(current_browser, &web_contents,
-                                        nullptr)) {
-      return RespondNow(Error(kUnknownErrorDoNotUse));
     }
-
-    browser = current_browser;
   } else {
     int tab_id = *params->tab_id;
 
+    Browser* browser = nullptr;
     std::string error;
     if (!GetTabById(tab_id, browser_context(), include_incognito_information(),
                     &browser, nullptr, &web_contents, nullptr, &error)) {
@@ -2023,7 +2019,7 @@
       return RespondNow(Error(tabs_constants::kGroupParamsError));
 
     group_id = *params->options.group_id;
-    if (!tab_groups_util::GetGroupById(
+    if (!ExtensionTabUtil::GetGroupById(
             group_id, browser_context(), include_incognito_information(),
             &target_browser, &group, nullptr, &error)) {
       return RespondNow(Error(std::move(error)));
@@ -2034,8 +2030,13 @@
         params->options.create_properties->window_id) {
       window_id = *params->options.create_properties->window_id;
     }
-    if (!GetBrowserFromWindowID(this, window_id, &target_browser, &error))
+    WindowController* target_controller =
+        ExtensionTabUtil::GetControllerFromWindowID(
+            ChromeExtensionFunctionDetails(this), window_id, &error);
+    if (!target_controller) {
       return RespondNow(Error(std::move(error)));
+    }
+    target_browser = target_controller->GetBrowser();
   }
 
   DCHECK(target_browser);
@@ -2109,7 +2110,7 @@
   }
   if (group.is_empty()) {
     group = tab_strip->AddToNewGroup(tab_indices);
-    group_id = tab_groups_util::GetGroupId(group);
+    group_id = ExtensionTabUtil::GetGroupId(group);
   } else {
     tab_strip->AddToExistingGroup(tab_indices, group);
   }
@@ -2194,10 +2195,14 @@
 WebContents* TabsCaptureVisibleTabFunction::GetWebContentsForID(
     int window_id,
     std::string* error) {
-  Browser* browser = nullptr;
-  if (!GetBrowserFromWindowID(chrome_details_, window_id, &browser, error))
+  WindowController* window_controller =
+      ExtensionTabUtil::GetControllerFromWindowID(chrome_details_, window_id,
+                                                  error);
+  if (!window_controller) {
     return nullptr;
+  }
 
+  Browser* browser = window_controller->GetBrowser();
   TabStripModel* tab_strip_model =
       ExtensionTabUtil::GetEditableTabStripModel(browser);
   if (!tab_strip_model) {
@@ -2348,17 +2353,16 @@
       tabs::DetectLanguage::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  int tab_id = 0;
-  Browser* browser = nullptr;
   WebContents* contents = nullptr;
 
   // If |tab_id| is specified, look for it. Otherwise default to selected tab
   // in the current window.
-  std::string error;
   if (params->tab_id) {
-    tab_id = *params->tab_id;
-    if (!GetTabById(tab_id, browser_context(), include_incognito_information(),
-                    &browser, nullptr, &contents, nullptr, &error)) {
+    Browser* browser = nullptr;
+    std::string error;
+    if (!GetTabById(*params->tab_id, browser_context(),
+                    include_incognito_information(), &browser, nullptr,
+                    &contents, nullptr, &error)) {
       return RespondNow(Error(std::move(error)));
     }
     // The browser will be null for prerender tabs.
@@ -2366,16 +2370,18 @@
       return RespondNow(Error(kUnknownErrorDoNotUse));
     }
   } else {
-    browser = ChromeExtensionFunctionDetails(this).GetCurrentBrowser();
-    if (!browser)
+    WindowController* window_controller =
+        ChromeExtensionFunctionDetails(this).GetCurrentWindowController();
+    if (!window_controller) {
       return RespondNow(Error(tabs_constants::kNoCurrentWindowError));
-    TabStripModel* tab_strip_model =
-        ExtensionTabUtil::GetEditableTabStripModel(browser);
-    if (!tab_strip_model)
+    }
+    if (!ExtensionTabUtil::IsTabStripEditable()) {
       return RespondNow(Error(tabs_constants::kTabStripNotEditableError));
-    contents = tab_strip_model->GetActiveWebContents();
-    if (!contents)
+    }
+    contents = window_controller->GetActiveTab();
+    if (!contents) {
       return RespondNow(Error(tabs_constants::kNoSelectedTabError));
+    }
   }
 
   if (contents->GetController().NeedsReload()) {
@@ -2480,14 +2486,18 @@
   // If the tab ID wasn't given then it needs to be converted to the
   // currently active tab's ID.
   if (tab_id == -1) {
-    Browser* browser = chrome_details_.GetCurrentBrowser();
-    // Can happen during shutdown.
-    if (!browser)
+    if (WindowController* window_controller =
+            chrome_details_.GetCurrentWindowController()) {
+      content::WebContents* web_contents = window_controller->GetActiveTab();
+      if (!web_contents) {
+        // Can happen during shutdown.
+        return set_init_result_error(
+            tabs_constants::kNoTabInBrowserWindowError);
+      }
+      tab_id = ExtensionTabUtil::GetTabId(web_contents);
+    } else {
+      // Can happen during shutdown.
       return set_init_result_error(tabs_constants::kNoCurrentWindowError);
-    content::WebContents* web_contents = nullptr;
-    // Can happen during shutdown.
-    if (!ExtensionTabUtil::GetActiveTab(browser, &web_contents, &tab_id)) {
-      return set_init_result_error(tabs_constants::kNoTabInBrowserWindowError);
     }
   }
 
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index 7623765b..402fb713 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -21,7 +21,6 @@
 #include "base/test/values_test_util.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
@@ -1063,7 +1062,7 @@
 
   // Add a tab to a group to have an existing group ID.
   tab_groups::TabGroupId group = GetTabStripModel()->AddToNewGroup({1});
-  int group_id = tab_groups_util::GetGroupId(group);
+  int group_id = ExtensionTabUtil::GetGroupId(group);
 
   // Attempt to specify both createProperties and groupId.
   auto function = base::MakeRefCounted<TabsGroupFunction>();
@@ -1126,7 +1125,7 @@
 
   tab_groups::TabGroupId group2 =
       browser2->tab_strip_model()->AddToNewGroup({1});
-  int group_id2 = tab_groups_util::GetGroupId(group2);
+  int group_id2 = ExtensionTabUtil::GetGroupId(group2);
 
   // Use the TabsGroupFunction to group tabs 0, 2, and 4 from the original
   // browser into the same group as the one in browser2.
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index 7b4e80c9..98021a4 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -34,7 +34,6 @@
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/devtools/devtools_window_testing.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/browser_extension_window_controller.h"
@@ -754,7 +753,7 @@
   function->set_extension(ExtensionBuilder("Test").Build().get());
   constexpr char kFormatQueryArgs[] = R"([{"groupId":%d}])";
   const std::string args = base::StringPrintf(
-      kFormatQueryArgs, tab_groups_util::GetGroupId(group_id));
+      kFormatQueryArgs, ExtensionTabUtil::GetGroupId(group_id));
   base::Value::List result(
       utils::ToList(utils::RunFunctionAndReturnSingleResult(
           function.get(), args, browser()->profile())));
@@ -978,9 +977,14 @@
           api_test_utils::FunctionMode::kIncognito));
   int window_id = GetWindowId(result);
   std::string error;
-  Browser* new_window = ExtensionTabUtil::GetBrowserFromWindowID(
-      ChromeExtensionFunctionDetails(function.get()), window_id, &error);
+
+  WindowController* new_controller =
+      ExtensionTabUtil::GetControllerFromWindowID(
+          ChromeExtensionFunctionDetails(function.get()), window_id, &error);
+  ASSERT_TRUE(new_controller);
   EXPECT_TRUE(error.empty());
+  Browser* new_browser = new_controller->GetBrowser();
+  ASSERT_TRUE(new_browser);
 
 // TODO(crbug.com/40254339): Remove this workaround if this wait is no longer
 // needed.
@@ -994,7 +998,7 @@
   // from the window server
   views::test::PropertyWaiter minimize_waiter(
       base::BindRepeating(&BrowserWindow::IsMinimized,
-                          base::Unretained(new_window->window())),
+                          base::Unretained(new_browser->window())),
       true);
   EXPECT_TRUE(minimize_waiter.Wait());
 #elif BUILDFLAG(IS_OZONE_WAYLAND)
@@ -1002,7 +1006,7 @@
   // verification of IsMinimized() for as well.
 #endif
 #else
-  EXPECT_TRUE(new_window->window()->IsMinimized());
+  EXPECT_TRUE(new_controller->window()->IsMinimized());
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
   function = base::MakeRefCounted<WindowsCreateFunction>();
@@ -1012,10 +1016,12 @@
       function.get(), "[{\"state\": \"fullscreen\"}]", browser()->profile(),
       api_test_utils::FunctionMode::kIncognito));
   window_id = GetWindowId(result);
-  new_window = ExtensionTabUtil::GetBrowserFromWindowID(
+
+  new_controller = ExtensionTabUtil::GetControllerFromWindowID(
       ChromeExtensionFunctionDetails(function.get()), window_id, &error);
+  ASSERT_TRUE(new_controller);
   EXPECT_TRUE(error.empty());
-  EXPECT_TRUE(new_window->window()->IsFullscreen());
+  EXPECT_TRUE(new_controller->GetBrowser()->window()->IsFullscreen());
 
   // Let the message loop run so that |fake_fullscreen| finishes transition.
   content::RunAllPendingInMessageLoop();
@@ -1130,8 +1136,9 @@
       utils::ToDict(utils::RunFunctionAndReturnSingleResult(
           function.get(), R"([{"type": "popup"}])", browser()->profile()));
   int window_id = GetWindowId(result);
+
   std::string error;
-  EXPECT_TRUE(ExtensionTabUtil::GetBrowserFromWindowID(
+  EXPECT_TRUE(ExtensionTabUtil::GetControllerFromWindowID(
       ChromeExtensionFunctionDetails(function.get()), window_id, &error));
   EXPECT_TRUE(error.empty());
 }
diff --git a/chrome/browser/extensions/api/tabs/windows_util.cc b/chrome/browser/extensions/api/tabs/windows_util.cc
index d3119fe..3504a6f2 100644
--- a/chrome/browser/extensions/api/tabs/windows_util.cc
+++ b/chrome/browser/extensions/api/tabs/windows_util.cc
@@ -26,49 +26,47 @@
 
 namespace windows_util {
 
-bool GetBrowserFromWindowID(ExtensionFunction* function,
-                            int window_id,
-                            extensions::WindowController::TypeFilter filter,
-                            Browser** browser,
-                            std::string* error) {
-  DCHECK(browser);
+bool GetControllerFromWindowID(ExtensionFunction* function,
+                               int window_id,
+                               extensions::WindowController::TypeFilter filter,
+                               extensions::WindowController** out_controller,
+                               std::string* error) {
+  DCHECK(out_controller);
   DCHECK(error);
 
-  *browser = nullptr;
+  *out_controller = nullptr;
   if (window_id == extension_misc::kCurrentWindowId) {
     // If there is a window controller associated with this extension, use that.
-    extensions::WindowController* window_controller =
-        function->dispatcher()->GetExtensionWindowController();
-    if (!window_controller) {
-      // Otherwise get the focused or most recently added window.
-      window_controller =
-          extensions::WindowControllerList::GetInstance()
-              ->CurrentWindowForFunctionWithFilter(function, filter);
+    if (extensions::WindowController* window_controller =
+            function->dispatcher()->GetExtensionWindowController()) {
+      *out_controller = window_controller;
+      return true;
     }
 
-    if (window_controller)
-      *browser = window_controller->GetBrowser();
-
-    if (!(*browser)) {
-      *error = extensions::tabs_constants::kNoCurrentWindowError;
-      return false;
+    // Otherwise get the focused or most recently added window.
+    if (extensions::WindowController* window_controller =
+            extensions::WindowControllerList::GetInstance()
+                ->CurrentWindowForFunctionWithFilter(function, filter)) {
+      *out_controller = window_controller;
+      return true;
     }
+
+    *error = extensions::tabs_constants::kNoCurrentWindowError;
+    return false;
   } else {
-    extensions::WindowController* window_controller =
-        extensions::WindowControllerList::GetInstance()
-            ->FindWindowForFunctionByIdWithFilter(function, window_id, filter);
-    if (window_controller)
-      *browser = window_controller->GetBrowser();
-
-    if (!(*browser)) {
-      *error = extensions::ErrorUtils::FormatErrorMessage(
-          extensions::tabs_constants::kWindowNotFoundError,
-          base::NumberToString(window_id));
-      return false;
+    if (extensions::WindowController* window_controller =
+            extensions::WindowControllerList::GetInstance()
+                ->FindWindowForFunctionByIdWithFilter(function, window_id,
+                                                      filter)) {
+      *out_controller = window_controller;
+      return true;
     }
+
+    *error = extensions::ErrorUtils::FormatErrorMessage(
+        extensions::tabs_constants::kWindowNotFoundError,
+        base::NumberToString(window_id));
+    return false;
   }
-  DCHECK(*browser);
-  return true;
 }
 
 bool CanOperateOnWindow(const ExtensionFunction* function,
diff --git a/chrome/browser/extensions/api/tabs/windows_util.h b/chrome/browser/extensions/api/tabs/windows_util.h
index 02cff31d..3d232e2 100644
--- a/chrome/browser/extensions/api/tabs/windows_util.h
+++ b/chrome/browser/extensions/api/tabs/windows_util.h
@@ -8,25 +8,22 @@
 #include <string>
 #include <vector>
 
+#include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/extensions/window_controller_list.h"
 
 class ExtensionFunction;
 class Profile;
 class GURL;
 
-namespace extensions {
-class WindowController;
-}
-
 namespace windows_util {
 
-// Populates `browser` for given `window_id`. If the window is not found,
+// Populates `*controller` for given `window_id`. If the window is not found,
 // returns false and sets `error`.
-bool GetBrowserFromWindowID(ExtensionFunction* function,
-                            int window_id,
-                            extensions::WindowController::TypeFilter filter,
-                            Browser** browser,
-                            std::string* error);
+bool GetControllerFromWindowID(ExtensionFunction* function,
+                               int window_id,
+                               extensions::WindowController::TypeFilter filter,
+                               extensions::WindowController** controller,
+                               std::string* error);
 
 // Returns true if `function` (and the profile and extension that it was
 // invoked from) can operate on the window wrapped by `window_controller`.
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 6ecadde7..e537b29 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -22,9 +22,9 @@
 #include "base/metrics/user_metrics_action.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/lock/screen_locker.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/crosapi/mojom/clipboard_history.mojom.h"
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index a821b98..09c410f 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -37,7 +37,6 @@
 #include "chrome/browser/extensions/mv2_experiment_stage.h"
 #include "chrome/browser/extensions/scoped_active_install.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/safe_browsing/safe_browsing_metrics_collector_factory.h"
 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager_factory.h"
@@ -66,6 +65,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/extension_util.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_constants.h"
@@ -731,9 +731,9 @@
     case ExtensionInstallPrompt::Result::ACCEPTED_WITH_WITHHELD_PERMISSIONS: {
       // TODO(b/202064235): The only user of this branch is ChromeOs v1 flow.
       // Handle parent permission for child accounts on ChromeOS.
-      if (!dummy_extension_->is_theme()  // Parent permission not required for
-                                         // theme installation
-          && g_browser_process->profile_manager()->IsValidProfile(profile_) &&
+      // Parent permission not required for theme installation.
+      if (!dummy_extension_->is_theme() &&
+          ExtensionsBrowserClient::Get()->IsValidContext(profile_) &&
           supervised_user::AreExtensionsPermissionsEnabled(profile_) &&
           !supervised_user::SupervisedUserCanSkipExtensionParentApprovals(
               profile_)) {
@@ -1324,8 +1324,9 @@
   }
 
   Profile* const profile = Profile::FromBrowserContext(browser_context());
-  if (!g_browser_process->profile_manager()->IsValidProfile(profile)) {
+  if (!ExtensionsBrowserClient::Get()->IsValidContext(profile)) {
     Respond(Error(kWebstoreUserCancelledError));
+    return;
   }
 
   std::string error;
diff --git a/chrome/browser/extensions/browser_extension_window_controller.cc b/chrome/browser/extensions/browser_extension_window_controller.cc
index 0bcd5b0..0c97737 100644
--- a/chrome/browser/extensions/browser_extension_window_controller.cc
+++ b/chrome/browser/extensions/browser_extension_window_controller.cc
@@ -77,20 +77,16 @@
   return browser_;
 }
 
-bool BrowserExtensionWindowController::GetActiveTab(
-    content::WebContents** contents,
-    int* optional_tab_id) const {
-  DCHECK(contents);
+bool BrowserExtensionWindowController::IsDeleteScheduled() const {
+  return browser_->is_delete_scheduled();
+}
 
-  *contents = browser_->tab_strip_model()->GetActiveWebContents();
-  if (*contents) {
-    if (optional_tab_id) {
-      *optional_tab_id = ExtensionTabUtil::GetTabId(*contents);
-    }
-    return true;
-  }
+content::WebContents* BrowserExtensionWindowController::GetActiveTab() const {
+  return browser_->tab_strip_model()->GetActiveWebContents();
+}
 
-  return false;
+bool BrowserExtensionWindowController::HasEditableTabStrip() const {
+  return browser_->window()->IsTabStripEditable();
 }
 
 bool BrowserExtensionWindowController::IsVisibleToTabsAPIForExtension(
diff --git a/chrome/browser/extensions/browser_extension_window_controller.h b/chrome/browser/extensions/browser_extension_window_controller.h
index ab19aee..33cc1d3 100644
--- a/chrome/browser/extensions/browser_extension_window_controller.h
+++ b/chrome/browser/extensions/browser_extension_window_controller.h
@@ -35,8 +35,9 @@
                          const GURL& extension_url) const override;
   bool CanClose(Reason* reason) const override;
   Browser* GetBrowser() const override;
-  bool GetActiveTab(content::WebContents** contents,
-                    int* tab_id) const override;
+  bool IsDeleteScheduled() const override;
+  content::WebContents* GetActiveTab() const override;
+  bool HasEditableTabStrip() const override;
   bool IsVisibleToTabsAPIForExtension(
       const Extension* extension,
       bool allow_dev_tools_windows) const override;
diff --git a/chrome/browser/extensions/chrome_extension_function_details.cc b/chrome/browser/extensions/chrome_extension_function_details.cc
index e2be8bb5..c50882a 100644
--- a/chrome/browser/extensions/chrome_extension_function_details.cc
+++ b/chrome/browser/extensions/chrome_extension_function_details.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 
+#include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/extensions/window_controller_list.h"
 #include "chrome/browser/profiles/profile.h"
@@ -26,15 +27,16 @@
 ChromeExtensionFunctionDetails::~ChromeExtensionFunctionDetails() {
 }
 
-Browser* ChromeExtensionFunctionDetails::GetCurrentBrowser() const {
-  // If the delegate has an associated browser, return it.
+extensions::WindowController*
+ChromeExtensionFunctionDetails::GetCurrentWindowController() const {
+  // If the delegate has an associated window controller, return it.
   if (function_->dispatcher()) {
-    extensions::WindowController* window_controller =
-        function_->dispatcher()->GetExtensionWindowController();
-    if (window_controller) {
-      if (auto* browser = window_controller->GetBrowser();
-          browser && !browser->is_delete_scheduled()) {
-        return browser;
+    if (extensions::WindowController* window_controller =
+            function_->dispatcher()->GetExtensionWindowController()) {
+      // Only return the found controller if it's not about to be deleted,
+      // otherwise fall through to finding another one.
+      if (!window_controller->IsDeleteScheduled()) {
+        return window_controller;
       }
     }
   }
@@ -53,7 +55,7 @@
   Browser* browser = chrome::FindAnyBrowser(
       profile, function_->include_incognito_information());
   if (browser)
-    return browser;
+    return browser->extension_window_controller();
 
   // NOTE(rafaelw): This can return NULL in some circumstances. In particular,
   // a background_page onload chrome.tabs api call can make it into here
diff --git a/chrome/browser/extensions/chrome_extension_function_details.h b/chrome/browser/extensions/chrome_extension_function_details.h
index 7c0cd45..6dc8c420 100644
--- a/chrome/browser/extensions/chrome_extension_function_details.h
+++ b/chrome/browser/extensions/chrome_extension_function_details.h
@@ -8,9 +8,12 @@
 #include "base/memory/raw_ptr.h"
 #include "ui/gfx/native_widget_types.h"
 
-class Browser;
 class ExtensionFunction;
 
+namespace extensions {
+class WindowController;
+}
+
 // Provides Chrome-specific details to ExtensionFunction
 // implementations.
 class ChromeExtensionFunctionDetails {
@@ -27,12 +30,12 @@
 
   ~ChromeExtensionFunctionDetails();
 
-  // Gets the "current" browser, if any.
+  // Gets the "current" WindowController, if any.
   //
-  // Many extension APIs operate relative to the current browser, which is the
-  // browser the calling code is running inside of. For example, popups and tabs
-  // all have a containing browser, but background pages and notification
-  // bubbles do not.
+  // Many extension APIs operate relative to the current window, which is the
+  // browser window the calling code is running inside of. For example, popups
+  // and tabs all have a containing browser, but background pages and
+  // notification bubbles do not.
   //
   // If there is no containing window, the current browser defaults to the
   // foremost one.
@@ -40,14 +43,14 @@
   // Incognito browsers are not considered unless the calling extension has
   // incognito access enabled.
   //
-  // This method can return NULL if there is no matching browser, which can
-  // happen if only incognito windows are open, or early in startup or shutdown
-  // shutdown when there are no active windows.
+  // This method can return NULL if there is no matching browser window, which
+  // can happen if only incognito windows are open, or early in startup or
+  // shutdown shutdown when there are no active windows.
   //
   // TODO(devlin): This method is incredibly non-deterministic (sometimes just
   // returning "any" browser), and almost never the right thing to use. Instead,
   // use ExtensionFunction::GetSenderWebContents(). We should get rid of this.
-  Browser* GetCurrentBrowser() const;
+  extensions::WindowController* GetCurrentWindowController() const;
 
   // Find a UI surface to display any UI (like a permission prompt) for the
   // extension calling this function. This will check, in order of preference,
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.cc b/chrome/browser/extensions/chrome_extension_host_delegate.cc
index d56c004..63a60ec 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.cc
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.cc
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "chrome/browser/apps/platform_apps/audio_focus_web_contents_observer.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
@@ -17,6 +16,7 @@
 #include "components/javascript_dialogs/app_modal_dialog_manager.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/common/extension_id.h"
 
 namespace extensions {
@@ -53,8 +53,9 @@
   // Verify that the browser is not shutting down. It can be the case if the
   // call is propagated through a posted task that was already in the queue when
   // shutdown started. See crbug.com/625646
-  if (g_browser_process->IsShuttingDown())
+  if (ExtensionsBrowserClient::Get()->IsShuttingDown()) {
     return;
+  }
 
   ExtensionTabUtil::CreateTab(std::move(web_contents), extension_id,
                               disposition, window_features, user_gesture);
diff --git a/chrome/browser/extensions/chrome_process_manager_delegate.cc b/chrome/browser/extensions/chrome_process_manager_delegate.cc
index bb1d8d3..8c11b58 100644
--- a/chrome/browser/extensions/chrome_process_manager_delegate.cc
+++ b/chrome/browser/extensions/chrome_process_manager_delegate.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/user_manager/user_manager.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_manager_factory.h"
 #include "extensions/common/extension.h"
@@ -102,15 +103,10 @@
 
 bool ChromeProcessManagerDelegate::DeferCreatingStartupBackgroundHosts(
     content::BrowserContext* context) const {
-  Profile* profile = Profile::FromBrowserContext(context);
-
   // The profile may not be valid yet if it is still being initialized.
   // In that case, defer loading, since it depends on an initialized profile.
   // Background hosts will be loaded later via OnProfileAdded.
-  // http://crbug.com/222473
-  // Unit tests may not have a profile manager.
-  return (g_browser_process->profile_manager() &&
-          !g_browser_process->profile_manager()->IsValidProfile(profile));
+  return !ExtensionsBrowserClient::Get()->IsValidContext(context);
 }
 
 void ChromeProcessManagerDelegate::OnBrowserAdded(Browser* browser) {
diff --git a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browsertest.cc b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browsertest.cc
index b7dac90..2c086ef0 100644
--- a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browsertest.cc
+++ b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browsertest.cc
@@ -9,6 +9,7 @@
 #include "chrome/test/base/chrome_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/file_util.h"
@@ -18,27 +19,6 @@
 
 namespace extensions {
 
-namespace {
-
-// Attempts to load (parse) an extension from the given `file_path`, returning
-// it on success. On failure, adds a test failure.
-scoped_refptr<const Extension> LoadExtensionFromDirectory(
-    const base::FilePath& file_path) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  std::string load_error;
-  scoped_refptr<const Extension> extension = file_util::LoadExtension(
-      file_path, mojom::ManifestLocation::kUnpacked, 0, &load_error);
-  if (!extension) {
-    ADD_FAILURE() << "Failed to create extension: " << load_error;
-    return nullptr;
-  }
-
-  return extension;
-}
-
-}  // namespace
-
 class DesktopAndroidExtensionsBrowserTest : public AndroidBrowserTest {
  public:
   DesktopAndroidExtensionsBrowserTest() = default;
@@ -54,6 +34,42 @@
     host_resolver()->AddRule("*", "127.0.0.1");
     ASSERT_TRUE(embedded_test_server()->Start());
   }
+
+  // Returns the main `BrowserContext`.
+  content::BrowserContext* GetBrowserContext() {
+    return GetActiveWebContents()->GetBrowserContext();
+  }
+
+  // Attempts to parse and load an extension from the given `file_path` and add
+  // it to the extensions system (which will also activate the extension).
+  // Returns the extension on success; on failure, returns null and adds a test
+  // failure.
+  const Extension* LoadExtensionFromDirectory(const base::FilePath& file_path) {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+
+    std::string load_error;
+    scoped_refptr<const Extension> extension = file_util::LoadExtension(
+        file_path, mojom::ManifestLocation::kUnpacked, 0, &load_error);
+    if (!extension) {
+      ADD_FAILURE() << "Failed to parse extension: " << load_error;
+      return nullptr;
+    }
+
+    content::BrowserContext* browser_context =
+        GetActiveWebContents()->GetBrowserContext();
+
+    auto* android_system = static_cast<DesktopAndroidExtensionSystem*>(
+        ExtensionSystem::Get(browser_context));
+    android_system->AddExtension(extension);
+
+    ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context);
+    if (!registry->enabled_extensions().Contains(extension->id())) {
+      ADD_FAILURE() << "Extension is not properly enabled.";
+      return nullptr;
+    }
+
+    return extension.get();
+  }
 };
 
 // The following is a simple test exercising a basic navigation and script
@@ -128,14 +144,6 @@
       LoadExtensionFromDirectory(test_dir.UnpackedPath());
   ASSERT_TRUE(extension);
 
-  content::BrowserContext* browser_context =
-      GetActiveWebContents()->GetBrowserContext();
-
-  auto* android_system = static_cast<DesktopAndroidExtensionSystem*>(
-      ExtensionSystem::Get(browser_context));
-  ASSERT_TRUE(android_system);
-  android_system->AddExtension(extension);
-
   GURL extension_page = extension->GetResourceURL("page.html");
 
   ResultCatcher result_catcher;
@@ -146,4 +154,33 @@
   EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
 }
 
+// Test service worker-based extensions properly load and have the service
+// worker initialize and run.
+IN_PROC_BROWSER_TEST_F(DesktopAndroidExtensionsBrowserTest,
+                       ServiceWorkerBasedExtension) {
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Test Extension",
+           "version": "0.1",
+           "manifest_version": 3,
+           "background": {"service_worker": "background.js"}
+         })";
+  static constexpr char kBackgroundJs[] =
+      R"(chrome.test.runTests([
+           function sanityCheck() {
+             chrome.test.assertEq(2, 1 + 1);
+             chrome.test.succeed();
+           }
+         ]);)";
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs);
+
+  ResultCatcher result_catcher;
+  scoped_refptr<const Extension> extension =
+      LoadExtensionFromDirectory(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+  ASSERT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index 3c98854..1687e36 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -7,11 +7,13 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <cmath>
 #include <memory>
 #include <optional>
 #include <utility>
 
 #include "base/containers/fixed_flat_set.h"
+#include "base/hash/hash.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
@@ -19,7 +21,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/types/expected_macros.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/browser_extension_window_controller.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
@@ -41,6 +42,7 @@
 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
+#include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_utils.h"
 #include "chrome/browser/ui/ui_features.h"
@@ -81,6 +83,8 @@
 
 namespace {
 
+constexpr char kGroupNotFoundError[] = "No group with id: *.";
+
 // This enum is used for counting schemes used via a navigation triggered by
 // extensions.
 enum class NavigationScheme {
@@ -224,9 +228,13 @@
   // windowId defaults to "current" window.
   int window_id = params.window_id.value_or(extension_misc::kCurrentWindowId);
 
+  Browser* browser = nullptr;
   std::string error;
-  Browser* browser = GetBrowserFromWindowID(chrome_details, window_id, &error);
-  if (!browser) {
+  if (WindowController* controller =
+          GetControllerFromWindowID(chrome_details, window_id, &error)) {
+    browser = controller->GetBrowser();
+  } else {
+    // No matching window.
     if (!params.create_browser_if_needed)
       return base::unexpected(error);
 
@@ -248,10 +256,9 @@
   WebContents* opener = nullptr;
   Browser* opener_browser = nullptr;
   if (params.opener_tab_id) {
-    if (!ExtensionTabUtil::GetTabById(*params.opener_tab_id, profile,
-                                      function->include_incognito_information(),
-                                      &opener_browser, nullptr, &opener,
-                                      nullptr)) {
+    if (!GetTabById(*params.opener_tab_id, profile,
+                    function->include_incognito_information(), &opener_browser,
+                    nullptr, &opener, nullptr)) {
       return base::unexpected(ErrorUtils::FormatErrorMessage(
           tabs_constants::kTabNotFoundError,
           base::NumberToString(*params.opener_tab_id)));
@@ -264,9 +271,9 @@
 
   GURL url(chrome::kChromeUINewTabURL);
   if (params.url) {
-    ASSIGN_OR_RETURN(url, ExtensionTabUtil::PrepareURLForNavigation(
-                              *params.url, function->extension(),
-                              function->browser_context()));
+    ASSIGN_OR_RETURN(url,
+                     PrepareURLForNavigation(*params.url, function->extension(),
+                                             function->browser_context()));
   }
 
   // Default to foreground for the new tab. The presence of 'active' property
@@ -355,26 +362,26 @@
       .ToValue();
 }
 
-Browser* ExtensionTabUtil::GetBrowserFromWindowID(
+WindowController* ExtensionTabUtil::GetControllerFromWindowID(
     const ChromeExtensionFunctionDetails& details,
     int window_id,
     std::string* error) {
   if (window_id == extension_misc::kCurrentWindowId) {
-    Browser* result = details.GetCurrentBrowser();
-    if (!result || !result->window()) {
-      if (error)
-        *error = tabs_constants::kNoCurrentWindowError;
-      return nullptr;
+    if (WindowController* window_controller =
+            details.GetCurrentWindowController()) {
+      return window_controller;
     }
-    return result;
-  } else {
-    return GetBrowserInProfileWithId(
-        Profile::FromBrowserContext(details.function()->browser_context()),
-        window_id, details.function()->include_incognito_information(), error);
+    if (error) {
+      *error = tabs_constants::kNoCurrentWindowError;
+    }
+    return nullptr;
   }
+  return GetControllerInProfileWithId(
+      Profile::FromBrowserContext(details.function()->browser_context()),
+      window_id, details.function()->include_incognito_information(), error);
 }
 
-Browser* ExtensionTabUtil::GetBrowserInProfileWithId(
+WindowController* ExtensionTabUtil::GetControllerInProfileWithId(
     Profile* profile,
     int window_id,
     bool also_match_incognito_profile,
@@ -385,10 +392,11 @@
           : nullptr;
   for (Browser* browser : *BrowserList::GetInstance()) {
     if ((browser->profile() == profile ||
-         browser->profile() == incognito_profile) &&
-        ExtensionTabUtil::GetWindowId(browser) == window_id &&
-        browser->window()) {
-      return browser;
+         browser->profile() == incognito_profile)) {
+      WindowController* controller = WindowControllerFromBrowser(browser);
+      if (controller->GetWindowId() == window_id) {
+        return controller;
+      }
     }
   }
 
@@ -453,7 +461,7 @@
     std::optional<tab_groups::TabGroupId> group =
         tab_strip->GetTabGroupForTab(tab_index);
     if (group.has_value()) {
-      tab_object.group_id = tab_groups_util::GetGroupId(group.value());
+      tab_object.group_id = GetGroupId(group.value());
     }
   }
 
@@ -640,11 +648,8 @@
   return false;
 }
 
-bool ExtensionTabUtil::GetActiveTab(Browser* browser,
-                                    WebContents** contents,
-                                    int* optional_tab_id) {
-  return WindowControllerFromBrowser(browser)->GetActiveTab(contents,
-                                                            optional_tab_id);
+content::WebContents* ExtensionTabUtil::GetActiveTab(Browser* browser) {
+  return WindowControllerFromBrowser(browser)->GetActiveTab();
 }
 
 // static
@@ -765,6 +770,165 @@
 }
 
 // static
+int ExtensionTabUtil::GetGroupId(const tab_groups::TabGroupId& id) {
+  uint32_t hash = base::PersistentHash(id.ToString());
+  return std::abs(static_cast<int>(hash));
+}
+
+// static
+int ExtensionTabUtil::GetWindowIdOfGroup(const tab_groups::TabGroupId& id) {
+  Browser* browser = chrome::FindBrowserWithGroup(id, nullptr);
+  if (browser) {
+    return browser->session_id().id();
+  }
+  return -1;
+}
+
+// static
+bool ExtensionTabUtil::GetGroupById(
+    int group_id,
+    content::BrowserContext* browser_context,
+    bool include_incognito,
+    Browser** browser,
+    tab_groups::TabGroupId* id,
+    const tab_groups::TabGroupVisualData** visual_data,
+    std::string* error) {
+  if (group_id == -1) {
+    return false;
+  }
+
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  Profile* incognito_profile =
+      include_incognito && profile->HasPrimaryOTRProfile()
+          ? profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)
+          : nullptr;
+  for (Browser* target_browser : *BrowserList::GetInstance()) {
+    if (target_browser->profile() == profile ||
+        target_browser->profile() == incognito_profile) {
+      TabStripModel* target_tab_strip = target_browser->tab_strip_model();
+      if (!target_tab_strip->SupportsTabGroups()) {
+        continue;
+      }
+      for (tab_groups::TabGroupId target_group :
+           target_tab_strip->group_model()->ListTabGroups()) {
+        if (ExtensionTabUtil::GetGroupId(target_group) == group_id) {
+          if (browser) {
+            *browser = target_browser;
+          }
+          if (id) {
+            *id = target_group;
+          }
+          if (visual_data) {
+            *visual_data = target_tab_strip->group_model()
+                               ->GetTabGroup(target_group)
+                               ->visual_data();
+          }
+          return true;
+        }
+      }
+    }
+  }
+
+  *error = ErrorUtils::FormatErrorMessage(kGroupNotFoundError,
+                                          base::NumberToString(group_id));
+
+  return false;
+}
+
+// static
+api::tab_groups::TabGroup ExtensionTabUtil::CreateTabGroupObject(
+    const tab_groups::TabGroupId& id,
+    const tab_groups::TabGroupVisualData& visual_data) {
+  api::tab_groups::TabGroup tab_group_object;
+  tab_group_object.id = GetGroupId(id);
+  tab_group_object.collapsed = visual_data.is_collapsed();
+  tab_group_object.color = ColorIdToColor(visual_data.color());
+  tab_group_object.title = base::UTF16ToUTF8(visual_data.title());
+  tab_group_object.window_id = GetWindowIdOfGroup(id);
+
+  return tab_group_object;
+}
+
+// static
+std::optional<api::tab_groups::TabGroup> ExtensionTabUtil::CreateTabGroupObject(
+    const tab_groups::TabGroupId& id) {
+  Browser* browser = chrome::FindBrowserWithGroup(id, nullptr);
+  if (!browser) {
+    return std::nullopt;
+  }
+
+  CHECK(browser->tab_strip_model()->SupportsTabGroups());
+  TabGroupModel* group_model = browser->tab_strip_model()->group_model();
+  const tab_groups::TabGroupVisualData* visual_data =
+      group_model->GetTabGroup(id)->visual_data();
+
+  DCHECK(visual_data);
+
+  return CreateTabGroupObject(id, *visual_data);
+}
+
+// static
+api::tab_groups::Color ExtensionTabUtil::ColorIdToColor(
+    const tab_groups::TabGroupColorId& color_id) {
+  switch (color_id) {
+    case tab_groups::TabGroupColorId::kGrey:
+      return api::tab_groups::Color::kGrey;
+    case tab_groups::TabGroupColorId::kBlue:
+      return api::tab_groups::Color::kBlue;
+    case tab_groups::TabGroupColorId::kRed:
+      return api::tab_groups::Color::kRed;
+    case tab_groups::TabGroupColorId::kYellow:
+      return api::tab_groups::Color::kYellow;
+    case tab_groups::TabGroupColorId::kGreen:
+      return api::tab_groups::Color::kGreen;
+    case tab_groups::TabGroupColorId::kPink:
+      return api::tab_groups::Color::kPink;
+    case tab_groups::TabGroupColorId::kPurple:
+      return api::tab_groups::Color::kPurple;
+    case tab_groups::TabGroupColorId::kCyan:
+      return api::tab_groups::Color::kCyan;
+    case tab_groups::TabGroupColorId::kOrange:
+      return api::tab_groups::Color::kOrange;
+    case tab_groups::TabGroupColorId::kNumEntries:
+      NOTREACHED_IN_MIGRATION() << "kNumEntries is not a support color enum.";
+      return api::tab_groups::Color::kGrey;
+  }
+
+  NOTREACHED_IN_MIGRATION();
+  return api::tab_groups::Color::kCyan;
+}
+
+// static
+tab_groups::TabGroupColorId ExtensionTabUtil::ColorToColorId(
+    api::tab_groups::Color color) {
+  switch (color) {
+    case api::tab_groups::Color::kGrey:
+      return tab_groups::TabGroupColorId::kGrey;
+    case api::tab_groups::Color::kBlue:
+      return tab_groups::TabGroupColorId::kBlue;
+    case api::tab_groups::Color::kRed:
+      return tab_groups::TabGroupColorId::kRed;
+    case api::tab_groups::Color::kYellow:
+      return tab_groups::TabGroupColorId::kYellow;
+    case api::tab_groups::Color::kGreen:
+      return tab_groups::TabGroupColorId::kGreen;
+    case api::tab_groups::Color::kPink:
+      return tab_groups::TabGroupColorId::kPink;
+    case api::tab_groups::Color::kPurple:
+      return tab_groups::TabGroupColorId::kPurple;
+    case api::tab_groups::Color::kCyan:
+      return tab_groups::TabGroupColorId::kCyan;
+    case api::tab_groups::Color::kOrange:
+      return tab_groups::TabGroupColorId::kOrange;
+    case api::tab_groups::Color::kNone:
+      NOTREACHED_IN_MIGRATION();
+  }
+
+  NOTREACHED_IN_MIGRATION();
+  return tab_groups::TabGroupColorId::kGrey;
+}
+
+// static
 std::vector<content::WebContents*>
 ExtensionTabUtil::GetAllActiveWebContentsForContext(
     content::BrowserContext* browser_context,
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h
index 3fd1e810..caa663f2 100644
--- a/chrome/browser/extensions/extension_tab_util.h
+++ b/chrome/browser/extensions/extension_tab_util.h
@@ -13,7 +13,9 @@
 #include "base/types/expected.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/window_controller.h"
+#include "chrome/common/extensions/api/tab_groups.h"
 #include "chrome/common/extensions/api/tabs.h"
+#include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "extensions/common/features/feature.h"
 #include "extensions/common/mojom/context_type.mojom-forward.h"
@@ -34,6 +36,11 @@
 class WindowFeatures;
 }
 
+namespace tab_groups {
+class TabGroupId;
+class TabGroupVisualData;
+}  // namespace tab_groups
+
 namespace extensions {
 class Extension;
 class WindowController;
@@ -83,7 +90,7 @@
                                          const Extension* extension,
                                          mojom::ContextType context);
 
-  static Browser* GetBrowserFromWindowID(
+  static WindowController* GetControllerFromWindowID(
       const ChromeExtensionFunctionDetails& details,
       int window_id,
       std::string* error_message);
@@ -92,10 +99,11 @@
   // `profile`. Optionally, this will also look at browsers associated with the
   // incognito version of `profile` if `also_match_incognito_profile` is true.
   // Populates `error_message` if no matching browser is found.
-  static Browser* GetBrowserInProfileWithId(Profile* profile,
-                                            int window_id,
-                                            bool also_match_incognito_profile,
-                                            std::string* error_message);
+  static WindowController* GetControllerInProfileWithId(
+      Profile* profile,
+      int window_id,
+      bool also_match_incognito_profile,
+      std::string* error_message);
 
   // Returns the tabs:: API constant for the window type of the |browser|.
   static std::string GetBrowserWindowTypeText(const Browser& browser);
@@ -160,12 +168,9 @@
                                TabStripModel** tab_strip_model,
                                int* tab_index);
 
-  // On success, returns true and fills in the WebContents and extensions API
-  // tab ID for the active tab. The optional_tab_id may be null if the caller
-  // doesn't need it. Returns false if there is no active tab.
-  static bool GetActiveTab(Browser* browser,
-                           content::WebContents** contents,
-                           int* optional_tab_id);
+  // Returns the active tab's WebContents if there is an active tab. Returns
+  // null if there is no active tab.
+  static content::WebContents* GetActiveTab(Browser* browser);
 
   // Any out parameter (|browser|, |tab_strip|, |contents|, & |tab_index|) may
   // be NULL and will not be set within the function.
@@ -183,6 +188,41 @@
                          content::BrowserContext* browser_context,
                          bool include_incognito,
                          content::WebContents** contents);
+
+  // Gets the extensions-specific Group ID.
+  static int GetGroupId(const tab_groups::TabGroupId& id);
+
+  // Gets the window ID that the group belongs to.
+  static int GetWindowIdOfGroup(const tab_groups::TabGroupId& id);
+
+  // Gets the metadata for the group with ID `group_id`. Sets the `error` if not
+  // found. `browser`, `id`, or `visual_data` may be nullptr and will not be set
+  // within the function if so.
+  static bool GetGroupById(int group_id,
+                           content::BrowserContext* browser_context,
+                           bool include_incognito,
+                           Browser** browser,
+                           tab_groups::TabGroupId* id,
+                           const tab_groups::TabGroupVisualData** visual_data,
+                           std::string* error);
+
+  // Creates a TabGroup object
+  // (see chrome/common/extensions/api/tab_groups.json) with information about
+  // the state of a tab group for the given group `id`. Most group metadata is
+  // derived from the `visual_data`, which specifies group color, title, etc.
+  static api::tab_groups::TabGroup CreateTabGroupObject(
+      const tab_groups::TabGroupId& id,
+      const tab_groups::TabGroupVisualData& visual_data);
+  static std::optional<api::tab_groups::TabGroup> CreateTabGroupObject(
+      const tab_groups::TabGroupId& id);
+
+  // Conversions between the api::tab_groups::Color enum and the TabGroupColorId
+  // enum.
+  static api::tab_groups::Color ColorIdToColor(
+      const tab_groups::TabGroupColorId& color_id);
+  static tab_groups::TabGroupColorId ColorToColorId(
+      api::tab_groups::Color color);
+
   // Returns all active web contents for the given |browser_context|.
   static std::vector<content::WebContents*> GetAllActiveWebContentsForContext(
       content::BrowserContext* browser_context,
diff --git a/chrome/browser/extensions/service_worker_lifetime_keepalive_browsertest.cc b/chrome/browser/extensions/service_worker_lifetime_keepalive_browsertest.cc
index b381292caa..10f5c9e5 100644
--- a/chrome/browser/extensions/service_worker_lifetime_keepalive_browsertest.cc
+++ b/chrome/browser/extensions/service_worker_lifetime_keepalive_browsertest.cc
@@ -10,13 +10,11 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/permissions/permissions_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_management_test_util.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/test/test_browser_closed_waiter.h"
 #include "chrome/common/chrome_features.h"
@@ -30,6 +28,7 @@
 #include "extensions/browser/background_script_executor.h"
 #include "extensions/browser/browsertest_util.h"
 #include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/service_worker/service_worker_keepalive.h"
 #include "extensions/browser/service_worker/service_worker_test_utils.h"
 #include "extensions/common/extension.h"
@@ -771,7 +770,7 @@
   base::RunLoop().RunUntilIdle();
   // Verify the profile is destroyed.
   EXPECT_FALSE(
-      g_browser_process->profile_manager()->IsValidProfile(incognito_profile));
+      ExtensionsBrowserClient::Get()->IsValidContext(incognito_profile));
   // The test succeeds if there are no crashes. There's nothing left to verify
   // for keepalives, since the profile is gone.
 }
@@ -918,7 +917,7 @@
   base::RunLoop().RunUntilIdle();
   // Verify the profile is destroyed.
   EXPECT_FALSE(
-      g_browser_process->profile_manager()->IsValidProfile(incognito_profile));
+      ExtensionsBrowserClient::Get()->IsValidContext(incognito_profile));
   // The test succeeds if there are no crashes. There's nothing left to verify
   // for keepalives, since the profile is gone.
 }
@@ -1083,7 +1082,7 @@
 
   // Verify the profile is destroyed.
   EXPECT_FALSE(
-      g_browser_process->profile_manager()->IsValidProfile(incognito_profile));
+      ExtensionsBrowserClient::Get()->IsValidContext(incognito_profile));
 
   // Verify that all keepalives have been removed, since the message port was
   // closed as part of the incognito profile shutdown. (We can't verify
diff --git a/chrome/browser/extensions/window_controller.h b/chrome/browser/extensions/window_controller.h
index f1d21961..a7a76ae 100644
--- a/chrome/browser/extensions/window_controller.h
+++ b/chrome/browser/extensions/window_controller.h
@@ -97,11 +97,25 @@
   // TODO(stevenjb): Temporary workaround. Eliminate this.
   virtual Browser* GetBrowser() const;
 
-  // On success, returns true and fills in the WebContents and extensions API
-  // tab ID for the active tab. The optional_tab_id may be null if the caller
-  // doesn't need it. Returns false if there is no active tab.
-  virtual bool GetActiveTab(content::WebContents** contents,
-                            int* optional_tab_id) const = 0;
+  // Returns true if the window is in the process of being torn down. See
+  // Browser::is_delete_scheduled().
+  virtual bool IsDeleteScheduled() const = 0;
+
+  // Returns the WebContents associated with the active tab, if any. Returns
+  // null if there is no active tab.
+  virtual content::WebContents* GetActiveTab() const = 0;
+
+  // Returns true if this window has a tab strip that's currently editable or
+  // if there's no visible tab strip.
+  //
+  // During some animations and drags the tab strip won't be editable and
+  // extensions should not update it. Many callers should use
+  // ExtensionTabUtil::IsTabStripEditable() which will check *all* tab strips
+  // because some move operations span tab strips. This checking of all windows
+  // is why windows that don't have visible tab strips should still return true
+  // here: otherwise they will prevent some operations from happening that use
+  // the ExtensionTabUtil.
+  virtual bool HasEditableTabStrip() const = 0;
 
   // Returns true if the window is visible to the tabs API, when used by the
   // given |extension|.
diff --git a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsMediator.java b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsMediator.java
index c4a38959..2fda268 100644
--- a/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsMediator.java
+++ b/chrome/browser/facilitated_payments/ui/android/internal/java/src/org/chromium/chrome/browser/facilitated_payments/FacilitatedPaymentsPaymentMethodsMediator.java
@@ -42,6 +42,7 @@
 import org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.FooterProperties;
 import org.chromium.chrome.browser.facilitated_payments.FacilitatedPaymentsPaymentMethodsProperties.HeaderProperties;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.autofill.payments.AccountType;
 import org.chromium.components.autofill.payments.BankAccount;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
@@ -193,7 +194,7 @@
                             .getCustomImageForAutofillSuggestionIfAvailable(
                                     bankAccount.getDisplayIconUrl(),
                                     AutofillUiUtils.CardIconSpecs.create(
-                                            context, AutofillUiUtils.CardIconSize.SQUARE));
+                                            context, ImageSize.SQUARE));
         }
         if (bankIconOptional.isPresent()) {
             bankAccountModelBuilder.with(BANK_ACCOUNT_ICON_BITMAP, bankIconOptional.get());
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index ae37eb4..50fca350 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -161,14 +161,6 @@
   // away so they don't try and call back to us.
   if (select_file_dialog_)
     select_file_dialog_->ListenerDestroyed();
-
-#if !BUILDFLAG(IS_ANDROID)
-  if (has_notified_picture_in_picture_window_manager_of_open_dialog_) {
-    PictureInPictureWindowManager::GetInstance()->OnFileDialogClosed();
-    has_notified_picture_in_picture_window_manager_of_open_dialog_ = false;
-    scoped_disallow_picture_in_picture_.reset();
-  }
-#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
 void FileSelectHelper::FileSelected(const ui::SelectedFileInfo& file,
@@ -576,9 +568,6 @@
   content::WebContentsObserver::Observe(web_contents_);
 
 #if !BUILDFLAG(IS_ANDROID)
-  PictureInPictureWindowManager::GetInstance()->OnFileDialogOpened();
-  has_notified_picture_in_picture_window_manager_of_open_dialog_ = true;
-
   if (PictureInPictureWindowManager::GetInstance()
           ->ShouldFileDialogBlockPictureInPicture(web_contents_)) {
     scoped_disallow_picture_in_picture_ =
@@ -762,11 +751,7 @@
   }
 
 #if !BUILDFLAG(IS_ANDROID)
-  if (has_notified_picture_in_picture_window_manager_of_open_dialog_) {
-    PictureInPictureWindowManager::GetInstance()->OnFileDialogClosed();
-    has_notified_picture_in_picture_window_manager_of_open_dialog_ = false;
-    scoped_disallow_picture_in_picture_.reset();
-  }
+  scoped_disallow_picture_in_picture_.reset();
 #endif  // !BUILDFLAG(IS_ANDROID)
 
   Release();
diff --git a/chrome/browser/file_select_helper.h b/chrome/browser/file_select_helper.h
index 8809ed6..138889a 100644
--- a/chrome/browser/file_select_helper.h
+++ b/chrome/browser/file_select_helper.h
@@ -325,11 +325,6 @@
   bool abort_on_missing_web_contents_in_tests_ = true;
 
 #if !BUILDFLAG(IS_ANDROID)
-  // True if we've called `PictureInPictureWindowManager::OnFileDialogOpened()`
-  // but have not yet called
-  // `PictureInPictureWindowManager::OnFileDialogClosed()`.
-  bool has_notified_picture_in_picture_window_manager_of_open_dialog_ = false;
-
   // When not null, this prevents picture-in-picture windows from opening.
   std::unique_ptr<ScopedDisallowPictureInPicture>
       scoped_disallow_picture_in_picture_;
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index 09e20cd..a16b7ca 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -52,7 +52,9 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
-#if !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/path_utils.h"
+#else
 #include "chrome/browser/permissions/one_time_permissions_tracker_observer.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
@@ -99,7 +101,6 @@
 using ContentAnalysisResponse = enterprise_connectors::ContentAnalysisResponse;
 #endif
 
-#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
 namespace {
 
 enum class CreateSymbolicLinkResult {
@@ -150,6 +151,7 @@
       this};
 };
 
+#if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
 constexpr char kDummyDmToken[] = "dm_token";
 
 void EnableEnterpriseAnalysis(Profile* profile) {
@@ -174,6 +176,7 @@
 bool CreateNonEmptyFile(const base::FilePath& path) {
   return base::WriteFile(path, "data");
 }
+#endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
 
 #if BUILDFLAG(IS_WIN)
 CreateSymbolicLinkResult CreateWinSymbolicLink(const base::FilePath& target,
@@ -224,7 +227,6 @@
 }
 
 }  // namespace
-#endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
 
 class TestFileSystemAccessPermissionContext
     : public ChromeFileSystemAccessPermissionContext {
@@ -243,8 +245,15 @@
 class ChromeFileSystemAccessPermissionContextTest : public testing::Test {
  public:
   ChromeFileSystemAccessPermissionContextTest() {
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if BUILDFLAG(IS_ANDROID)
+    scoped_feature_list_.InitWithFeatures(
+        {}, {features::kFileSystemAccessPersistentPermissions});
+#else
     scoped_feature_list_.InitWithFeatures(
         {features::kFileSystemAccessPersistentPermissions}, {});
+#endif
   }
   void SetUp() override {
     // Create a scoped directory under %TEMP% instead of using
@@ -829,7 +838,6 @@
 }
 #endif  // BUILDFLAG(IS_ANDROID)
 
-#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_ResolveSymbolicLink) {
   if (!base::FeatureList::IsEnabled(
@@ -872,7 +880,6 @@
                 HandleType::kFile, UserAction::kOpen),
             SensitiveDirectoryResult::kAllowed);
 }
-#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_DangerousFile) {
@@ -911,8 +918,6 @@
       SensitiveDirectoryResult::kAbort);
 }
 
-// TODO(crbug.com/40101963): Enable android tests.
-#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        CanObtainWritePermission_ContentSettingAsk) {
   SetDefaultContentSettingValue(ContentSettingsType::FILE_SYSTEM_WRITE_GUARD,
@@ -1174,7 +1179,6 @@
                 .path,
             new_path);
 }
-#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetWellKnownDirectoryPath_Base_OK) {
@@ -1194,17 +1198,20 @@
             temp_dir_.GetPath());
 }
 
-// TODO(crbug.com/40101963): Enable android tests.
-#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetWellKnownDirectoryPath_Pdf_Downloads) {
+  base::FilePath expected_downloads = temp_dir_.GetPath();
   DownloadPrefs::FromBrowserContext(browser_context())
       ->SkipSanitizeDownloadTargetPathForTesting();
   DownloadPrefs::FromBrowserContext(browser_context())
       ->SetDownloadPath(temp_dir_.GetPath());
+#if BUILDFLAG(IS_ANDROID)
+  // Android always uses the system Download directory (/storage/emulated/...).
+  ASSERT_TRUE(base::android::GetDownloadsDirectory(&expected_downloads));
+#endif
   EXPECT_EQ(permission_context()->GetWellKnownDirectoryPath(
                 blink::mojom::WellKnownDirectory::kDirDownloads, kPdfOrigin),
-            temp_dir_.GetPath());
+            expected_downloads);
 }
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
@@ -1216,6 +1223,9 @@
       kTestOrigin, kTestPath, HandleType::kFile, GrantType::kRead));
 }
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetReadPermissionGrant_InitialState_Open_File) {
   permission_context()->SetOriginHasExtendedPermissionForTesting(kTestOrigin);
@@ -1225,6 +1235,7 @@
   EXPECT_TRUE(permission_context()->HasExtendedPermissionForTesting(
       kTestOrigin, kTestPath, HandleType::kFile, GrantType::kRead));
 }
+#endif
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetReadPermissionGrant_InitialState_Open_Directory) {
@@ -1262,6 +1273,9 @@
       kTestOrigin, kTestPath, HandleType::kDirectory, GrantType::kWrite));
 }
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetWritePermissionGrant_InitialState_WritableImplicitState) {
   permission_context()->SetOriginHasExtendedPermissionForTesting(kTestOrigin);
@@ -1355,7 +1369,8 @@
   EXPECT_FALSE(permission_context()->IsValidObject(grant));
 }
 
-// TODO(crbug.com/40101963): Enable android tests.
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
 #if !BUILDFLAG(IS_ANDROID)
 TEST_F(
     ChromeFileSystemAccessPermissionContextTest,
@@ -1481,6 +1496,7 @@
   EXPECT_TRUE(permission_context()->HasExtendedPermissionForTesting(
       kTestOrigin, kTestPath, HandleType::kFile, GrantType::kWrite));
 }
+#endif
 
 TEST_F(
     ChromeFileSystemAccessPermissionContextTest,
@@ -1538,6 +1554,9 @@
   EXPECT_EQ(kTestPath, granted_paths[0]);
 }
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetOriginsWithGrants_ForGrantedActiveGrantsOnly) {
   auto grant = permission_context()->GetReadPermissionGrant(
@@ -1611,6 +1630,7 @@
   EXPECT_EQ(grant1_from_storage->GetStatus(), PermissionStatus::GRANTED);
   EXPECT_EQ(grant2_from_storage->GetStatus(), PermissionStatus::GRANTED);
 }
+#endif
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        RestorePermissionPrompt_NotTriggered_HandleNotLoadedFromStorage) {
@@ -1679,6 +1699,9 @@
             PermissionRequestOutcome::kGrantedByRestorePrompt);
 }
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(
     ChromeFileSystemAccessPermissionContextTest,
     RestorePermissionPrompt_NotTriggered_WhenRequestingWriteAccessToReadGrant) {
@@ -1856,6 +1879,10 @@
               ContentSettingsType::FILE_SYSTEM_ACCESS_RESTORE_PERMISSION);
   EXPECT_TRUE(origin_is_embargoed_after_rejection_limit);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
+
+// TODO(crbug.com/40101963): Enable when android webapps integration is done.
+#if !BUILDFLAG(IS_ANDROID)
 
 class ChromeFileSystemAccessPermissionContextTestWithWebApp
     : public ChromeFileSystemAccessPermissionContextTest {
@@ -1996,7 +2023,11 @@
       permission_context()->GetPersistedGrantStatusForTesting(kTestOrigin),
       PersistedGrantStatus::kCurrent);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ToggleExtendedPermissionByUser) {
   auto read_grant = permission_context()->GetReadPermissionGrant(
@@ -2152,6 +2183,7 @@
   ASSERT_THAT(permission_context()->GetGrantedObjects(kTestOrigin),
               testing::SizeIs(1));
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(
     ChromeFileSystemAccessPermissionContextNoPersistenceTest,
@@ -2212,6 +2244,9 @@
       kTestOrigin, kTestPath, HandleType::kFile, GrantType::kWrite));
 }
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetReadPermissionGrant_InheritFromAncestor) {
   permission_context()->SetOriginHasExtendedPermissionForTesting(kTestOrigin);
@@ -2399,6 +2434,7 @@
   EXPECT_TRUE(permission_context()->HasExtendedPermissionForTesting(
       kTestOrigin, file_path, HandleType::kFile, GrantType::kWrite));
 }
+#endif
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        PersistedPermission_RevokeGrantByFilePath) {
@@ -2415,6 +2451,9 @@
       kTestOrigin, kTestPath, HandleType::kFile, GrantType::kWrite));
 }
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        PersistedPermission_NotAccessibleIfContentSettingBlock) {
   permission_context()->SetOriginHasExtendedPermissionForTesting(kTestOrigin);
@@ -2462,6 +2501,7 @@
   EXPECT_TRUE(permission_context()->HasExtendedPermissionForTesting(
       kTestOrigin, kTestPath, HandleType::kDirectory, GrantType::kWrite));
 }
+#endif
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        RequestPermission_ClearOutdatedDormantGrants) {
@@ -2495,6 +2535,9 @@
   EXPECT_EQ(grant->GetStatus(), PermissionStatus::GRANTED);
 }
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        RequestPermission_Dismissed) {
   base::HistogramTester histograms;
@@ -2610,6 +2653,7 @@
   EXPECT_TRUE(permission_context()->HasExtendedPermissionForTesting(
       kTestOrigin, kTestPath, HandleType::kFile, GrantType::kWrite));
 }
+#endif
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        RequestPermission_GlobalGuardBlockedBeforeOpenGrant) {
@@ -2793,6 +2837,9 @@
   EXPECT_EQ(grant4->GetStatus(), PermissionStatus::GRANTED);
 }
 
+// TODO(crbug.com/40101963): Enable when android persisted permissions are
+// implemented.
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        GetReadPermissionGrant_FileBecomesDirectory) {
   permission_context()->SetOriginHasExtendedPermissionForTesting(kTestOrigin);
@@ -2956,6 +3003,7 @@
   EXPECT_TRUE(permission_context()->HasExtendedPermissionForTesting(
       kTestOrigin, new_path, HandleType::kFile, GrantType::kWrite));
 }
+#endif
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ReadGrantDestroyedOnRevokeActiveGrants) {
@@ -3240,5 +3288,3 @@
 }
 
 #endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
-
-#endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/file_system_access/file_system_access_features.cc b/chrome/browser/file_system_access/file_system_access_features.cc
index b1a25e9..fe3c40e 100644
--- a/chrome/browser/file_system_access/file_system_access_features.cc
+++ b/chrome/browser/file_system_access/file_system_access_features.cc
@@ -18,7 +18,11 @@
 // Enables persistent permissions for the File System Access API.
 BASE_FEATURE(kFileSystemAccessPersistentPermissions,
              "kFileSystemAccessPersistentPermissions",
+#if BUILDFLAG(IS_ANDROID)
+             base::FEATURE_DISABLED_BY_DEFAULT);
+#else
              base::FEATURE_ENABLED_BY_DEFAULT);
+#endif
 
 // Enables the updated Page Info UI for the persistent permissions feature.
 BASE_FEATURE(kFileSystemAccessPersistentPermissionsUpdatedPageInfo,
diff --git a/chrome/browser/file_system_access/file_system_access_permission_context_factory.cc b/chrome/browser/file_system_access/file_system_access_permission_context_factory.cc
index aad55d2..fde4583 100644
--- a/chrome/browser/file_system_access/file_system_access_permission_context_factory.cc
+++ b/chrome/browser/file_system_access/file_system_access_permission_context_factory.cc
@@ -13,28 +13,16 @@
 ChromeFileSystemAccessPermissionContext*
 FileSystemAccessPermissionContextFactory::GetForProfile(
     content::BrowserContext* profile) {
-#if BUILDFLAG(IS_ANDROID)
-  // TODO(crbug.com/40101963): Local FS portion of FSA API is not yet enabled on
-  // Android. Create the permission context instance when supported on Android.
-  return nullptr;
-#else
   return static_cast<ChromeFileSystemAccessPermissionContext*>(
       GetInstance()->GetServiceForBrowserContext(profile, true));
-#endif
 }
 
 // static
 ChromeFileSystemAccessPermissionContext*
 FileSystemAccessPermissionContextFactory::GetForProfileIfExists(
     content::BrowserContext* profile) {
-#if BUILDFLAG(IS_ANDROID)
-  // TODO(crbug.com/40101963): Local FS portion of FSA API is not yet enabled on
-  // Android. Create the permission context instance when supported on Android.
-  return nullptr;
-#else
   return static_cast<ChromeFileSystemAccessPermissionContext*>(
       GetInstance()->GetServiceForBrowserContext(profile, false));
-#endif
 }
 
 // static
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index a7075698f..15ba4189 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2647,7 +2647,7 @@
   {
     "name":"enable-bubble-corner-radius-update",
     "owners": ["amehfooz@google.com"],
-    "expiry_milestone": 128
+    "expiry_milestone": 134
   },
   {
     "name": "enable-builtin-hls",
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 284843c4..3e514b0 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -87,7 +87,6 @@
 // this array may either refer to features defined in the header of this file or
 // in other locations in the code base (e.g. chrome/, components/, etc).
 const base::Feature* const kFeaturesExposedToJava[] = {
-    &autofill::features::kAutofillAddressProfileSavePromptNicknameSupport,
     &autofill::features::kAutofillEnableRankingFormulaAddressProfiles,
     &autofill::features::kAutofillEnableRankingFormulaCreditCards,
     &autofill::features::kAutofillEnableNewCardArtAndNetworkImages,
@@ -970,7 +969,7 @@
 
 BASE_FEATURE(kTabStripLayoutOptimization,
              "TabStripLayoutOptimization",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kTabStripStartupRefactoring,
              "TabStripStartupRefactoring",
diff --git a/chrome/browser/flags/android/chrome_session_state.h b/chrome/browser/flags/android/chrome_session_state.h
index e58ec88..cabcbfff 100644
--- a/chrome/browser/flags/android/chrome_session_state.h
+++ b/chrome/browser/flags/android/chrome_session_state.h
@@ -22,6 +22,9 @@
   kMaxValue = NO_VISIBLE_TAB,
 };
 
+// Following enum should always be in sync with ChromeActivityType defined in
+// tools/metrics/histograms/metadata/android/enums.xml
+
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.flags
 enum class ActivityType {
   // Chrome is running as the Chrome Android Browser App (i.e., traditional
@@ -54,15 +57,16 @@
   //   https://chromium.googlesource.com/chromium/src/+/refs/heads/main/chrome/android/webapk/README.md
   kWebApk,
 
-  // Chrome is running embedded in another application as auth-dedicated tab.
-  // TODO(b/353517557): Add a link to a developer guide
-  kAuthTab,
-
   // Chrome has started running, but no tab has yet become visible (for example:
   // warm-up,
   // FRE, downloads manager shown in response to a notification click, etc).
   kPreFirstTab,
-  kMaxValue = kPreFirstTab,
+
+  // Chrome is running embedded in another application as auth-dedicated tab.
+  // TODO(b/353517557): Add a link to a developer guide
+  kAuthTab,
+
+  kMaxValue = kAuthTab,
 };
 
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.flags
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 33b7ed9..464e112 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -181,8 +181,6 @@
     public static final String ANIMATED_IMAGE_DRAG_SHADOW = "AnimatedImageDragShadow";
     public static final String APP_SPECIFIC_HISTORY = "AppSpecificHistory";
     public static final String ASYNC_NOTIFICATION_MANAGER = "AsyncNotificationManager";
-    public static final String AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT =
-            "AutofillAddressProfileSavePromptNicknameSupport";
     public static final String AUTOFILL_ALLOW_NON_HTTP_ACTIVATION =
             "AutofillAllowNonHttpActivation";
     public static final String AUTOFILL_ENABLE_CARD_ART_IMAGE = "AutofillEnableCardArtImage";
@@ -701,7 +699,7 @@
     public static final CachedFlag sTabStripIncognitoMigration =
             newCachedFlag(TAB_STRIP_INCOGNITO_MIGRATION, false);
     public static final CachedFlag sTabStripLayoutOptimization =
-            newCachedFlag(TAB_STRIP_LAYOUT_OPTIMIZATION, true);
+            newCachedFlag(TAB_STRIP_LAYOUT_OPTIMIZATION, false);
     public static final CachedFlag sTabStripStartupRefactoring =
             newCachedFlag(TAB_STRIP_STARTUP_REFACTORING, true);
     public static final CachedFlag sTabletToolbarReordering =
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
index ce0ea98..4424c1f 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
@@ -414,4 +414,23 @@
   std::move(callback).Run(allow && web_view_guest()->attached());
 }
 
+void ChromeWebViewPermissionHelperDelegate::RequestFullscreenPermission(
+    const url::Origin& requesting_origin,
+    WebViewPermissionHelper::PermissionResponseCallback callback) {
+  if (web_view_guest()->attached() &&
+      web_view_guest()->IsOwnedByControlledFrameEmbedder() &&
+      !IsFeatureEnabledByEmbedderPermissionsPolicy(
+          web_view_guest(), blink::mojom::PermissionsPolicyFeature::kFullscreen,
+          requesting_origin)) {
+    std::move(callback).Run(/*allow=*/false, /*user_input=*/"");
+    return;
+  }
+
+  base::Value::Dict request_info;
+  request_info.Set(webview::kOrigin, requesting_origin.GetURL().spec());
+  web_view_permission_helper()->RequestPermission(
+      WEB_VIEW_PERMISSION_TYPE_FULLSCREEN, std::move(request_info),
+      std::move(callback), /*allowed_by_default=*/false);
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
index 235b4880..e635eda 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
@@ -16,6 +16,10 @@
 #include "chrome/common/plugin.mojom.h"
 #endif
 
+namespace url {
+class Origin;
+}  // namespace url
+
 namespace extensions {
 class WebViewGuest;
 
@@ -70,6 +74,10 @@
       bool allowed_by_default,
       base::OnceCallback<void(bool)> callback) override;
 
+  void RequestFullscreenPermission(
+      const url::Origin& requesting_origin,
+      WebViewPermissionHelper::PermissionResponseCallback callback) override;
+
  private:
 #if BUILDFLAG(ENABLE_PLUGINS)
   // chrome::mojom::PluginAuthHost methods.
diff --git a/chrome/browser/ip_protection/ip_protection_core_host.cc b/chrome/browser/ip_protection/ip_protection_core_host.cc
index 9ce6353..c53237e 100644
--- a/chrome/browser/ip_protection/ip_protection_core_host.cc
+++ b/chrome/browser/ip_protection/ip_protection_core_host.cc
@@ -10,6 +10,7 @@
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
+#include "base/hash/hash.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/ranges/algorithm.h"
 #include "base/sequence_checker.h"
@@ -339,6 +340,9 @@
         result = kFailedBSAOther;
         break;
     }
+    base::UmaHistogramSparse(
+        "NetworkService.IpProtection.TryGetAuthTokensErrors",
+        base::PersistentHash(tokens.status().ToString()));
     VLOG(2) << "IPATP::OnFetchBlindSignedTokenCompleted got an error: "
             << static_cast<int>(result);
     TryGetAuthTokensComplete(std::nullopt, std::move(callback), result);
diff --git a/chrome/browser/ip_protection/ip_protection_core_host_unittest.cc b/chrome/browser/ip_protection/ip_protection_core_host_unittest.cc
index 5740068..e1e472a 100644
--- a/chrome/browser/ip_protection/ip_protection_core_host_unittest.cc
+++ b/chrome/browser/ip_protection/ip_protection_core_host_unittest.cc
@@ -14,12 +14,12 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
-#include "components/ip_protection/common/ip_protection_data_types.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/ip_protection/common/ip_protection_config_http.h"
 #include "components/ip_protection/common/ip_protection_core_host_helper.h"
+#include "components/ip_protection/common/ip_protection_data_types.h"
 #include "components/ip_protection/common/ip_protection_proxy_config_fetcher.h"
 #include "components/ip_protection/common/mock_blind_sign_auth.h"
 #include "components/prefs/testing_pref_service.h"
@@ -47,6 +47,8 @@
     "NetworkService.IpProtection.TryGetAuthTokensResult";
 constexpr char kOAuthTokenFetchHistogram[] =
     "NetworkService.IpProtection.OAuthTokenFetchTime";
+constexpr char kTryGetAuthTokensErrorHistogram[] =
+    "NetworkService.IpProtection.TryGetAuthTokensErrors";
 constexpr char kTokenBatchHistogram[] =
     "NetworkService.IpProtection.TokenBatchRequestTime";
 
@@ -449,6 +451,10 @@
   histogram_tester_.ExpectUniqueSample(
       kTryGetAuthTokensResultHistogram,
       ip_protection::TryGetAuthTokensResult::kFailedBSA400, 1);
+  histogram_tester_.ExpectUniqueSample(
+      kTryGetAuthTokensErrorHistogram,
+      4043967578,  // base::PersistentHash("INVALID_ARGUMENT: uhoh")
+      1);
   histogram_tester_.ExpectTotalCount(kOAuthTokenFetchHistogram, 1);
   histogram_tester_.ExpectTotalCount(kTokenBatchHistogram, 0);
 }
@@ -469,6 +475,10 @@
   histogram_tester_.ExpectUniqueSample(
       kTryGetAuthTokensResultHistogram,
       ip_protection::TryGetAuthTokensResult::kFailedBSA401, 1);
+  histogram_tester_.ExpectUniqueSample(
+      kTryGetAuthTokensErrorHistogram,
+      4264091263,  // base::PersistentHash("UNAUTHENTICATED: uhoh")
+      1);
   histogram_tester_.ExpectTotalCount(kOAuthTokenFetchHistogram, 1);
   histogram_tester_.ExpectTotalCount(kTokenBatchHistogram, 0);
 }
@@ -489,6 +499,11 @@
   histogram_tester_.ExpectUniqueSample(
       kTryGetAuthTokensResultHistogram,
       ip_protection::TryGetAuthTokensResult::kFailedBSA403, 1);
+  histogram_tester_.ExpectUniqueSample(
+      kTryGetAuthTokensErrorHistogram,
+      4104528123,  // base::PersistentHash("PERMISSION_DENIED: uhoh")
+      1);
+  // Failed to parse GetInitialDataResponse
   histogram_tester_.ExpectTotalCount(kOAuthTokenFetchHistogram, 1);
   histogram_tester_.ExpectTotalCount(kTokenBatchHistogram, 0);
 }
@@ -509,6 +524,10 @@
   histogram_tester_.ExpectUniqueSample(
       kTryGetAuthTokensResultHistogram,
       ip_protection::TryGetAuthTokensResult::kFailedBSAOther, 1);
+  histogram_tester_.ExpectUniqueSample(
+      kTryGetAuthTokensErrorHistogram,
+      2844845398,  // base::PersistentHash("UNKNOWN: uhoh")
+      1);
   histogram_tester_.ExpectTotalCount(kOAuthTokenFetchHistogram, 1);
   histogram_tester_.ExpectTotalCount(kTokenBatchHistogram, 0);
 }
diff --git a/chrome/browser/lacros/desk_template_client_lacros.h b/chrome/browser/lacros/desk_template_client_lacros.h
index 5343c81..e92cb88 100644
--- a/chrome/browser/lacros/desk_template_client_lacros.h
+++ b/chrome/browser/lacros/desk_template_client_lacros.h
@@ -11,6 +11,7 @@
 #include "chromeos/crosapi/mojom/desk_template.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "url/gurl.h"
 
 class Profile;
@@ -26,7 +27,7 @@
   // DeskTemplateClient:
   void CreateBrowserWithRestoredData(
       const gfx::Rect& bounds,
-      const ui::WindowShowState show_state,
+      const ui::mojom::WindowShowState show_state,
       crosapi::mojom::DeskTemplateStatePtr additional_state) override;
   void GetBrowserInformation(uint32_t serial,
                              const std::string& window_unique_id,
diff --git a/chrome/browser/lens/core/mojom/lens.mojom b/chrome/browser/lens/core/mojom/lens.mojom
index 5bf88d17..1d9694f3 100644
--- a/chrome/browser/lens/core/mojom/lens.mojom
+++ b/chrome/browser/lens/core/mojom/lens.mojom
@@ -41,12 +41,17 @@
 
 // LINT.ThenChange(//tools/metrics/histograms/metadata/lens/enums.xml:LensOverlayUserAction)
 
-// Factory method for creating a new WebUI page handler.
+// TODO(crbug.com/365448173): Split into separate mojom files for overlay
+// and side panel.
+// Factory method for creating a new WebUI page handler for lens overlay.
 interface LensPageHandlerFactory {
   // The WebUI calls this method when the page is first initialized.
   CreatePageHandler(pending_receiver<LensPageHandler> handler,
                     pending_remote<LensPage> page);
+};
 
+// Factory method for creating a new WebUI page handler for lens side panel.
+interface LensSidePanelPageHandlerFactory {
   // The side panel WebUI calls this method when the page is first initialized.
   CreateSidePanelPageHandler(
       pending_receiver<LensSidePanelPageHandler> handler,
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediator.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediator.java
index 2fd58710..ad72838 100644
--- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediator.java
+++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediator.java
@@ -576,13 +576,31 @@
             generalModuleList.add(ModuleType.SINGLE_TAB);
         }
 
+        boolean shouldAddEducationalTipModule =
+                filteredEnabledModuleSet.contains(ModuleType.EDUCATIONAL_TIP);
         for (@ModuleType Integer moduleType : filteredEnabledModuleSet) {
-            if (moduleType == ModuleType.PRICE_CHANGE || moduleType == ModuleType.SINGLE_TAB) {
+            if (moduleType == ModuleType.PRICE_CHANGE
+                    || moduleType == ModuleType.SINGLE_TAB
+                    || moduleType == ModuleType.EDUCATIONAL_TIP) {
                 continue;
             }
 
+            if (moduleType == ModuleType.TAB_RESUMPTION && shouldAddEducationalTipModule) {
+                // Insert the educational tip module before the tab resumption module if the tab
+                // resumption module is included in the fixed module list.
+                generalModuleList.add(ModuleType.EDUCATIONAL_TIP);
+                shouldAddEducationalTipModule = false;
+            }
+
             generalModuleList.add(moduleType);
         }
+
+        if (shouldAddEducationalTipModule) {
+            // If not already added, place the educational tip module at the end of the fixed module
+            // list.
+            generalModuleList.add(ModuleType.EDUCATIONAL_TIP);
+        }
+
         return generalModuleList;
     }
 
@@ -646,6 +664,10 @@
     InputContext createInputContext() {
         InputContext inputContext = new InputContext();
         for (@ModuleType int moduleType = 0; moduleType < ModuleType.NUM_ENTRIES; moduleType++) {
+            if (moduleType == ModuleType.EDUCATIONAL_TIP) {
+                continue;
+            }
+
             inputContext.addEntry(
                     HomeModulesMetricsUtils.getFreshnessInputContextString(moduleType),
                     ProcessedValue.fromFloat(
@@ -709,13 +731,29 @@
             List<String> orderedModuleLabels, Set<Integer> filteredEnabledModuleSet) {
         List<Integer> moduleList = new ArrayList<>();
 
+        boolean shouldAddEducationalTipModule =
+                filteredEnabledModuleSet.contains(ModuleType.EDUCATIONAL_TIP);
         for (String label : orderedModuleLabels) {
             @ModuleType
             int currentModuleType = HomeModulesMetricsUtils.convertLabelToModuleType(label);
             if (filteredEnabledModuleSet.contains(currentModuleType)) {
+                if (currentModuleType == ModuleType.TAB_RESUMPTION
+                        && shouldAddEducationalTipModule) {
+                    // Insert the educational tip module before the tab resumption module if the tab
+                    // resumption module is included in the return list.
+                    moduleList.add(ModuleType.EDUCATIONAL_TIP);
+                    shouldAddEducationalTipModule = false;
+                }
+
                 moduleList.add(currentModuleType);
             }
         }
+
+        if (shouldAddEducationalTipModule) {
+            // If not already added, place the educational tip module at the end of the return list.
+            moduleList.add(ModuleType.EDUCATIONAL_TIP);
+        }
+
         return moduleList;
     }
 
diff --git a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java
index 3fd8846..8c84c6d 100644
--- a/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java
+++ b/chrome/browser/magic_stack/android/java/src/org/chromium/chrome/browser/magic_stack/HomeModulesMetricsUtils.java
@@ -93,9 +93,6 @@
 
     private static final String SAFETY_HUB_FRESHNESS_INPUT_CONTEXT = "safety_hub_freshness";
 
-    private static final String EDUCATIONAL_TIP_FRESHNESS_INPUT_CONTEXT =
-            "educational_tip_freshness";
-
     private static final String HOME_MODULES_SHOW_ALL_MODULES_PARAM = "show_all_modules";
     public static final BooleanCachedFieldTrialParameter HOME_MODULES_SHOW_ALL_MODULES =
             ChromeFeatureList.newBooleanCachedFieldTrialParameter(
@@ -147,8 +144,6 @@
                 return TAB_RESUMPTION_FRESHNESS_INPUT_CONTEXT;
             case (SAFETY_HUB):
                 return SAFETY_HUB_FRESHNESS_INPUT_CONTEXT;
-            case (EDUCATIONAL_TIP):
-                return EDUCATIONAL_TIP_FRESHNESS_INPUT_CONTEXT;
             default:
                 assert false : "Module type not supported!";
                 return null;
diff --git a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java
index ba9c7602..767b8626 100644
--- a/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java
+++ b/chrome/browser/magic_stack/android/junit/src/org/chromium/chrome/browser/magic_stack/HomeModulesMediatorUnitTest.java
@@ -731,7 +731,7 @@
         scoreLoggedTime = SystemClock.elapsedRealtime() - 10;
         mHomeModulesConfigManager.setFreshnessScoreTimeStamp(moduleType, scoreLoggedTime);
         mHomeModulesConfigManager.setFreshnessCountForTesting(moduleType, expectedScore);
-        int[] scores = new int[] {-1, expectedScore, -1, -1, -1};
+        int[] scores = new int[] {-1, expectedScore, -1, -1};
         inputContext = mMediator.createInputContext();
         verifyInputContext(inputContext, scores);
 
@@ -751,28 +751,74 @@
         filteredEnabledModuleSet.add(ModuleType.SINGLE_TAB);
         filteredEnabledModuleSet.add(ModuleType.PRICE_CHANGE);
         filteredEnabledModuleSet.add(ModuleType.TAB_RESUMPTION);
+        filteredEnabledModuleSet.add(ModuleType.SAFETY_HUB);
+        filteredEnabledModuleSet.add(ModuleType.EDUCATIONAL_TIP);
 
         // Verifies the orders of modules match the heuristic logic when all modules are present.
         List<Integer> expectedModuleList =
-                List.of(ModuleType.PRICE_CHANGE, ModuleType.SINGLE_TAB, ModuleType.TAB_RESUMPTION);
+                List.of(
+                        ModuleType.PRICE_CHANGE,
+                        ModuleType.SINGLE_TAB,
+                        ModuleType.EDUCATIONAL_TIP,
+                        ModuleType.TAB_RESUMPTION,
+                        ModuleType.SAFETY_HUB);
         assertEquals(expectedModuleList, mMediator.getFixedModuleList(filteredEnabledModuleSet));
 
         // Verifies that Price change module is always placed as the first module.
         filteredEnabledModuleSet.remove(ModuleType.SINGLE_TAB);
-        expectedModuleList = List.of(ModuleType.PRICE_CHANGE, ModuleType.TAB_RESUMPTION);
+        expectedModuleList =
+                List.of(
+                        ModuleType.PRICE_CHANGE,
+                        ModuleType.EDUCATIONAL_TIP,
+                        ModuleType.TAB_RESUMPTION,
+                        ModuleType.SAFETY_HUB);
         assertEquals(expectedModuleList, mMediator.getFixedModuleList(filteredEnabledModuleSet));
 
-        // Verifies that single tab module is placed before the tab resumption module.
+        // Verifies that single tab module is placed before the educational tip module and tab
+        // resumption module.
         filteredEnabledModuleSet.add(ModuleType.SINGLE_TAB);
         filteredEnabledModuleSet.remove(ModuleType.PRICE_CHANGE);
-        expectedModuleList = List.of(ModuleType.SINGLE_TAB, ModuleType.TAB_RESUMPTION);
+        expectedModuleList =
+                List.of(
+                        ModuleType.SINGLE_TAB,
+                        ModuleType.EDUCATIONAL_TIP,
+                        ModuleType.TAB_RESUMPTION,
+                        ModuleType.SAFETY_HUB);
         assertEquals(expectedModuleList, mMediator.getFixedModuleList(filteredEnabledModuleSet));
 
-        // Verifies that the tab resumption module becomes the first module when the first two
-        // modules
+        // Verifies that the safety hub module becomes the first module when the first two modules
         // are disabled.
         filteredEnabledModuleSet.remove(ModuleType.SINGLE_TAB);
-        expectedModuleList = List.of(ModuleType.TAB_RESUMPTION);
+        expectedModuleList =
+                List.of(
+                        ModuleType.EDUCATIONAL_TIP,
+                        ModuleType.TAB_RESUMPTION,
+                        ModuleType.SAFETY_HUB);
+        assertEquals(expectedModuleList, mMediator.getFixedModuleList(filteredEnabledModuleSet));
+
+        // Verifies that the educational tip module becomes the last module when the tab resumption
+        // module is disabled.
+        filteredEnabledModuleSet.add(ModuleType.SINGLE_TAB);
+        filteredEnabledModuleSet.add(ModuleType.PRICE_CHANGE);
+        filteredEnabledModuleSet.remove(ModuleType.TAB_RESUMPTION);
+        expectedModuleList =
+                List.of(
+                        ModuleType.PRICE_CHANGE,
+                        ModuleType.SINGLE_TAB,
+                        ModuleType.SAFETY_HUB,
+                        ModuleType.EDUCATIONAL_TIP);
+        assertEquals(expectedModuleList, mMediator.getFixedModuleList(filteredEnabledModuleSet));
+
+        // Verifies that the tab resumption module stays as the last module when the educational tip
+        // module is disabled.
+        filteredEnabledModuleSet.add(ModuleType.TAB_RESUMPTION);
+        filteredEnabledModuleSet.remove(ModuleType.EDUCATIONAL_TIP);
+        expectedModuleList =
+                List.of(
+                        ModuleType.PRICE_CHANGE,
+                        ModuleType.SINGLE_TAB,
+                        ModuleType.TAB_RESUMPTION,
+                        ModuleType.SAFETY_HUB);
         assertEquals(expectedModuleList, mMediator.getFixedModuleList(filteredEnabledModuleSet));
     }
 
@@ -821,11 +867,16 @@
         filteredEnabledModuleSet.add(ModuleType.SINGLE_TAB);
         filteredEnabledModuleSet.add(ModuleType.PRICE_CHANGE);
         filteredEnabledModuleSet.add(ModuleType.TAB_RESUMPTION);
+        filteredEnabledModuleSet.add(ModuleType.EDUCATIONAL_TIP);
 
         // Verifies that result of #filterEnabledModuleList() is used if the segmentation
         // service returns a valid result.
         List<Integer> expectedModuleList =
-                List.of(ModuleType.TAB_RESUMPTION, ModuleType.SINGLE_TAB, ModuleType.PRICE_CHANGE);
+                List.of(
+                        ModuleType.EDUCATIONAL_TIP,
+                        ModuleType.TAB_RESUMPTION,
+                        ModuleType.SINGLE_TAB,
+                        ModuleType.PRICE_CHANGE);
         assertEquals(
                 expectedModuleList,
                 mMediator.filterEnabledModuleList(
@@ -838,7 +889,8 @@
         // Verifies that the disabled module will be removed from the result of the segmentation
         // service.
         filteredEnabledModuleSet.remove(ModuleType.TAB_RESUMPTION);
-        expectedModuleList = List.of(ModuleType.SINGLE_TAB, ModuleType.PRICE_CHANGE);
+        expectedModuleList =
+                List.of(ModuleType.SINGLE_TAB, ModuleType.PRICE_CHANGE, ModuleType.EDUCATIONAL_TIP);
         assertEquals(
                 expectedModuleList,
                 mMediator.filterEnabledModuleList(
@@ -860,20 +912,31 @@
     }
 
     private void verifyInputContext(InputContext inputContext, int[] scores) {
-        assertEquals(ModuleType.NUM_ENTRIES, inputContext.getSizeForTesting());
+        assertEquals(ModuleType.NUM_ENTRIES - 1, inputContext.getSizeForTesting());
+
+        int j = 0;
         for (int i = 0; i < ModuleType.NUM_ENTRIES; i++) {
+            if (i == ModuleType.EDUCATIONAL_TIP) {
+                continue;
+            }
+
             assertEquals(
-                    scores[i],
+                    scores[j],
                     inputContext.getEntryForTesting(
                                     HomeModulesMetricsUtils.getFreshnessInputContextString(i))
                             .floatValue,
                     0.01);
+            j = j + 1;
         }
     }
 
     private void verifyEmptyInputContext(InputContext inputContext) {
-        assertEquals(ModuleType.NUM_ENTRIES, inputContext.getSizeForTesting());
+        assertEquals(ModuleType.NUM_ENTRIES - 1, inputContext.getSizeForTesting());
         for (int i = 0; i < ModuleType.NUM_ENTRIES; i++) {
+            if (i == ModuleType.EDUCATIONAL_TIP) {
+                continue;
+            }
+
             assertEquals(
                     INVALID_FRESHNESS_SCORE,
                     inputContext.getEntryForTesting(
diff --git a/chrome/browser/media/chromeos_login_and_lock_media_access_handler.cc b/chrome/browser/media/chromeos_login_and_lock_media_access_handler.cc
index 4e8a524..1681e01 100644
--- a/chrome/browser/media/chromeos_login_and_lock_media_access_handler.cc
+++ b/chrome/browser/media/chromeos_login_and_lock_media_access_handler.cc
@@ -8,7 +8,7 @@
 
 #include "base/logging.h"
 #include "base/values.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_dialogs.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
diff --git a/chrome/browser/media/webrtc/BUILD.gn b/chrome/browser/media/webrtc/BUILD.gn
index abfad36..c3b91f7c 100644
--- a/chrome/browser/media/webrtc/BUILD.gn
+++ b/chrome/browser/media/webrtc/BUILD.gn
@@ -231,7 +231,6 @@
       "//ash",
       "//ash/public/cpp",
       "//chrome/browser/ash/arc/screen_capture",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/notifications",
       "//chrome/browser/ash/policy/handlers",
       "//chrome/browser/ash/policy/multi_screen_capture",
@@ -246,7 +245,6 @@
     ]
     allow_circular_includes_from = [
       "//chrome/browser/ash/policy/handlers",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/profiles",
       "//chrome/browser/ash/notifications",
       "//chrome/browser/ash/arc/screen_capture",
diff --git a/chrome/browser/metrics/chrome_android_metrics_provider_unittest.cc b/chrome/browser/metrics/chrome_android_metrics_provider_unittest.cc
index c35519c..e8c1fe1b 100644
--- a/chrome/browser/metrics/chrome_android_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/chrome_android_metrics_provider_unittest.cc
@@ -239,15 +239,17 @@
 
 // Tests initial transition from kPreFirstTab to !kPreFirstTab.
 TEST_P(ChromeAndroidMetricsProviderTest, TabSwitching) {
-  // kPreFirstTab -> kPreFirstTab is not a valid scenario. Early exit.
-  if (activity_type() == ActivityType::kPreFirstTab)
-    return;
-
   const auto first_activity_type = activity_type();
   const auto second_activity_type =
       static_cast<ActivityType>((static_cast<int>(first_activity_type) + 1) %
                                 static_cast<int>(ActivityType::kMaxValue));
 
+  // Transition to kPreFirstTab is not a valid scenario. Early exit.
+  if (first_activity_type == ActivityType::kPreFirstTab ||
+      second_activity_type == ActivityType::kPreFirstTab) {
+    return;
+  }
+
   // Validating startup, so seed the activity type to kPreFirstTab,
   SetInitialActivityTypeForTesting(ActivityType::kPreFirstTab);
 
@@ -286,5 +288,5 @@
                                          ActivityType::kTrustedWebActivity,
                                          ActivityType::kWebapp,
                                          ActivityType::kWebApk,
-                                         ActivityType::kAuthTab,
-                                         ActivityType::kPreFirstTab));
+                                         ActivityType::kPreFirstTab,
+                                         ActivityType::kAuthTab));
diff --git a/chrome/browser/metrics/per_user_state_manager_chromeos_browsertest.cc b/chrome/browser/metrics/per_user_state_manager_chromeos_browsertest.cc
index b7041e5b..babfffd 100644
--- a/chrome/browser/metrics/per_user_state_manager_chromeos_browsertest.cc
+++ b/chrome/browser/metrics/per_user_state_manager_chromeos_browsertest.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/ash/login/test/oobe_screens_utils.h"
 #include "chrome/browser/ash/login/test/scoped_policy_update.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
 #include "chrome/browser/ash/policy/core/device_policy_builder.h"
@@ -29,6 +28,7 @@
 #include "chrome/browser/metrics/profile_pref_names.h"
 #include "chrome/browser/metrics/testing/metrics_reporting_pref_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_prefs.cc b/chrome/browser/nearby_sharing/common/nearby_share_prefs.cc
index e5aa711..10c84ef 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_prefs.cc
+++ b/chrome/browser/nearby_sharing/common/nearby_share_prefs.cc
@@ -35,8 +35,14 @@
 const char kNearbySharingFullNamePrefName[] = "nearby_sharing.full_name";
 const char kNearbySharingIconUrlPrefName[] = "nearby_sharing.icon_url";
 const char kNearbySharingIconTokenPrefName[] = "nearby_sharing.icon_token";
+extern const char kNearbySharingInHighVisibilityPrefName[] =
+    "nearby_sharing.in_high_visibility";
 const char kNearbySharingNearbyDeviceTryingToShareDismissedTimePrefName[] =
     "nearby_sharing.nearby_device_trying_to_share_dismissed_time";
+extern const char kNearbySharingPreviousBackgroundVisibilityPrefName[] =
+    "nearby_sharing.previous_background_visibility";
+extern const char kNearbySharingPreviousInHighVisibilityPrefName[] =
+    "nearby_sharing.previous_in_high_visibility";
 const char kNearbySharingPublicCertificateExpirationDictPrefName[] =
     "nearbyshare.public_certificate_expiration_dict";
 const char kNearbySharingPrivateCertificateListPrefName[] =
@@ -76,6 +82,8 @@
       prefs::kNearbySharingFastInitiationNotificationStatePrefName,
       /*default_value=*/static_cast<int>(
           nearby_share::mojom::FastInitiationNotificationState::kEnabled));
+  registry->RegisterBooleanPref(prefs::kNearbySharingInHighVisibilityPrefName,
+                                /*default_value=*/false);
   registry->RegisterBooleanPref(prefs::kNearbySharingOnboardingCompletePrefName,
                                 /*default_value=*/false);
   registry->RegisterIntegerPref(
@@ -104,6 +112,13 @@
   registry->RegisterTimePref(
       prefs::kNearbySharingNextVisibilityReminderTimePrefName,
       /*default_value=*/base::Time());
+  registry->RegisterIntegerPref(
+      prefs::kNearbySharingPreviousBackgroundVisibilityPrefName,
+      /*default_value=*/static_cast<int>(
+          nearby_share::mojom::Visibility::kNoOne));
+  registry->RegisterBooleanPref(
+      prefs::kNearbySharingPreviousInHighVisibilityPrefName,
+      /*default_value=*/false);
   registry->RegisterDictionaryPref(
       prefs::kNearbySharingPublicCertificateExpirationDictPrefName);
   registry->RegisterListPref(
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_prefs.h b/chrome/browser/nearby_sharing/common/nearby_share_prefs.h
index 1fcb7ae2..722b932 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_prefs.h
+++ b/chrome/browser/nearby_sharing/common/nearby_share_prefs.h
@@ -22,8 +22,11 @@
 extern const char kNearbySharingFullNamePrefName[];
 extern const char kNearbySharingIconUrlPrefName[];
 extern const char kNearbySharingIconTokenPrefName[];
+extern const char kNearbySharingInHighVisibilityPrefName[];
 extern const char
     kNearbySharingNearbyDeviceTryingToShareDismissedTimePrefName[];
+extern const char kNearbySharingPreviousBackgroundVisibilityPrefName[];
+extern const char kNearbySharingPreviousInHighVisibilityPrefName[];
 extern const char kNearbySharingPrivateCertificateListPrefName[];
 extern const char kNearbySharingPublicCertificateExpirationDictPrefName[];
 extern const char kNearbySharingSchedulerContactDownloadAndUploadPrefName[];
diff --git a/chrome/browser/nearby_sharing/nearby_share_settings.cc b/chrome/browser/nearby_sharing/nearby_share_settings.cc
index a16b22a6..cea2bfd9 100644
--- a/chrome/browser/nearby_sharing/nearby_share_settings.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_settings.cc
@@ -218,6 +218,8 @@
 
 void NearbyShareSettings::SetVisibility(
     nearby_share::mojom::Visibility visibility) {
+  DCHECK(pref_service_);
+
   pref_service_->SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                             static_cast<int>(visibility));
 }
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
index 766dfbe..beed839d 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -24,6 +24,7 @@
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/nearby_sharing/certificates/common.h"
 #include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h"
@@ -770,7 +771,11 @@
 }
 
 bool NearbySharingServiceImpl::IsInHighVisibility() const {
-  return in_high_visibility;
+  if (chromeos::features::IsQuickShareV2Enabled()) {
+    return prefs_->GetBoolean(prefs::kNearbySharingInHighVisibilityPrefName);
+  }
+
+  return in_high_visibility_;
 }
 
 bool NearbySharingServiceImpl::IsTransferring() const {
@@ -4883,13 +4888,19 @@
 
 void NearbySharingServiceImpl::SetInHighVisibility(
     bool new_in_high_visibility) {
-  if (in_high_visibility == new_in_high_visibility) {
+  if (IsInHighVisibility() == new_in_high_visibility) {
     return;
   }
 
-  in_high_visibility = new_in_high_visibility;
+  if (chromeos::features::IsQuickShareV2Enabled()) {
+    prefs_->SetBoolean(prefs::kNearbySharingInHighVisibilityPrefName,
+                       /*value=*/new_in_high_visibility);
+  } else {
+    in_high_visibility_ = new_in_high_visibility;
+  }
+
   for (auto& observer : observers_) {
-    observer.OnHighVisibilityChanged(in_high_visibility);
+    observer.OnHighVisibilityChanged(new_in_high_visibility);
   }
 }
 
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
index ebd1c1f..d5be8886 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
@@ -427,7 +427,7 @@
       NearbyConnectionsManager::ConnectionsStatus status);
   void OnStartDiscoveryResult(
       NearbyConnectionsManager::ConnectionsStatus status);
-  void SetInHighVisibility(bool in_high_visibility);
+  void SetInHighVisibility(bool new_in_high_visibility);
 
   // Note: |share_target| is intentionally passed by value. A share target
   // reference could likely be invalidated by the owner during the multi-step
@@ -572,7 +572,7 @@
   // The time scanning began.
   base::Time scanning_start_timestamp_;
   // True when we are advertising with a device name visible to everyone.
-  bool in_high_visibility = false;
+  bool in_high_visibility_ = false;
   // The time attachments are sent after a share target is selected. This is
   // used to time the process from selecting a share target to writing the
   // introduction frame (last frame before receiver gets notified).
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
index 1cd831a0..762118b6 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -351,7 +351,8 @@
 
 // We will run tests with the following feature flags enabled and disabled in
 // all permutations. To add or a remove a feature you can just update this list.
-const std::vector<base::test::FeatureRef> kTestFeatures = {};
+const std::vector<base::test::FeatureRef> kTestFeatures = {
+    chromeos::features::kQuickShareV2};
 
 bool FileExists(const base::FilePath& file_path) {
   base::ScopedAllowBlockingForTesting allow_blocking;
diff --git a/chrome/browser/net/nss_context_chromeos_browsertest.cc b/chrome/browser/net/nss_context_chromeos_browsertest.cc
index 91d74cb..3290c194 100644
--- a/chrome/browser/net/nss_context_chromeos_browsertest.cc
+++ b/chrome/browser/net/nss_context_chromeos_browsertest.cc
@@ -12,11 +12,11 @@
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/net/nss_service.h"
 #include "chrome/browser/net/nss_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/test/base/ash/scoped_test_system_nss_key_slot_mixin.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/notifications/scheduler/internal/impression_types.cc b/chrome/browser/notifications/scheduler/internal/impression_types.cc
index 7908891..0ae2835 100644
--- a/chrome/browser/notifications/scheduler/internal/impression_types.cc
+++ b/chrome/browser/notifications/scheduler/internal/impression_types.cc
@@ -15,6 +15,12 @@
 
 Impression::Impression(const Impression& other) = default;
 
+Impression::Impression(Impression&& other) = default;
+
+Impression& Impression::operator=(const Impression& other) = default;
+
+Impression& Impression::operator=(Impression&& other) = default;
+
 Impression::~Impression() = default;
 
 bool Impression::operator==(const Impression& other) const {
@@ -48,6 +54,12 @@
 
 ClientState::ClientState(const ClientState& other) = default;
 
+ClientState::ClientState(ClientState&& other) = default;
+
+ClientState& ClientState::operator=(const ClientState& other) = default;
+
+ClientState& ClientState::operator=(ClientState&& other) = default;
+
 ClientState::~ClientState() = default;
 
 bool ClientState::operator==(const ClientState& other) const {
diff --git a/chrome/browser/notifications/scheduler/internal/impression_types.h b/chrome/browser/notifications/scheduler/internal/impression_types.h
index 527aac859..43b21b3 100644
--- a/chrome/browser/notifications/scheduler/internal/impression_types.h
+++ b/chrome/browser/notifications/scheduler/internal/impression_types.h
@@ -27,11 +27,15 @@
 struct Impression {
   using ImpressionResultMap = std::map<UserFeedback, ImpressionResult>;
   using CustomData = std::map<std::string, std::string>;
+
   Impression();
   Impression(SchedulerClientType type,
              const std::string& guid,
              const base::Time& create_time);
   Impression(const Impression& other);
+  Impression(Impression&& other);
+  Impression& operator=(const Impression& other);
+  Impression& operator=(Impression&& other);
   ~Impression();
 
   bool operator==(const Impression& other) const;
@@ -98,8 +102,13 @@
 // client.
 struct ClientState {
   using Impressions = base::circular_deque<Impression>;
+
   ClientState();
-  explicit ClientState(const ClientState& other);
+  ClientState(const ClientState& other);
+  ClientState(ClientState&& other);
+  ClientState& operator=(const ClientState& other);
+  ClientState& operator=(ClientState&& other);
+
   ~ClientState();
 
   bool operator==(const ClientState& other) const;
diff --git a/chrome/browser/notifications/scheduler/public/client_overview.cc b/chrome/browser/notifications/scheduler/public/client_overview.cc
index fc7261f..ecdbdf0 100644
--- a/chrome/browser/notifications/scheduler/public/client_overview.cc
+++ b/chrome/browser/notifications/scheduler/public/client_overview.cc
@@ -17,6 +17,13 @@
 
 ClientOverview::ClientOverview(const ClientOverview& other) = default;
 
+ClientOverview::ClientOverview(ClientOverview&& other) = default;
+
+ClientOverview& ClientOverview::operator=(const ClientOverview& other) =
+    default;
+
+ClientOverview& ClientOverview::operator=(ClientOverview&& other) = default;
+
 ClientOverview::~ClientOverview() = default;
 
 bool ClientOverview::operator==(const ClientOverview& other) const {
diff --git a/chrome/browser/notifications/scheduler/public/client_overview.h b/chrome/browser/notifications/scheduler/public/client_overview.h
index a0e2dfdb8..289604e 100644
--- a/chrome/browser/notifications/scheduler/public/client_overview.h
+++ b/chrome/browser/notifications/scheduler/public/client_overview.h
@@ -18,7 +18,11 @@
   ClientOverview(ImpressionDetail impression_detail,
                  size_t num_scheduled_notifications);
   ClientOverview(const ClientOverview& other);
+  ClientOverview(ClientOverview&& other);
+  ClientOverview& operator=(const ClientOverview& other);
+  ClientOverview& operator=(ClientOverview&& other);
   ~ClientOverview();
+
   bool operator==(const ClientOverview& other) const;
 
   // Details of impression.
diff --git a/chrome/browser/notifications/scheduler/public/icon_bundle.cc b/chrome/browser/notifications/scheduler/public/icon_bundle.cc
index b2fb3c9..468c03f8 100644
--- a/chrome/browser/notifications/scheduler/public/icon_bundle.cc
+++ b/chrome/browser/notifications/scheduler/public/icon_bundle.cc
@@ -8,11 +8,20 @@
 
 namespace notifications {
 
-IconBundle::IconBundle() : resource_id(0) {}
-IconBundle::IconBundle(SkBitmap skbitmap)
-    : bitmap(std::move(skbitmap)), resource_id(0) {}
+IconBundle::IconBundle() = default;
+
+IconBundle::IconBundle(SkBitmap skbitmap) : bitmap(std::move(skbitmap)) {}
+
 IconBundle::IconBundle(int resource_id) : resource_id(resource_id) {}
+
 IconBundle::IconBundle(const IconBundle& other) = default;
+
+IconBundle::IconBundle(IconBundle&& other) = default;
+
+IconBundle& IconBundle::operator=(const IconBundle& other) = default;
+
+IconBundle& IconBundle::operator=(IconBundle&& other) = default;
+
 IconBundle::~IconBundle() = default;
 
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/icon_bundle.h b/chrome/browser/notifications/scheduler/public/icon_bundle.h
index 30360f3d..fb8547f 100644
--- a/chrome/browser/notifications/scheduler/public/icon_bundle.h
+++ b/chrome/browser/notifications/scheduler/public/icon_bundle.h
@@ -15,6 +15,9 @@
   explicit IconBundle(SkBitmap skbitmap);
   explicit IconBundle(int resource_id);
   IconBundle(const IconBundle& other);
+  IconBundle(IconBundle&& other);
+  IconBundle& operator=(const IconBundle& other);
+  IconBundle& operator=(IconBundle&& other);
   ~IconBundle();
 
   // The icon bitmap.
@@ -22,7 +25,7 @@
 
   // Android resource Id. Do not set it until BeforeShowNotification. Default is
   // 0, representing no resource_id.
-  int resource_id;
+  int resource_id = 0;
 };
 
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.cc b/chrome/browser/notifications/scheduler/public/notification_data.cc
index 7c365a89..b3cf64c 100644
--- a/chrome/browser/notifications/scheduler/public/notification_data.cc
+++ b/chrome/browser/notifications/scheduler/public/notification_data.cc
@@ -7,24 +7,42 @@
 namespace notifications {
 
 NotificationData::Button::Button() : type(ActionButtonType::kUnknownAction) {}
+
 NotificationData::Button::Button(const Button& other) = default;
 
+NotificationData::Button::Button(Button&& other) = default;
+
+NotificationData::Button& NotificationData::Button::operator=(
+    const Button& other) = default;
+
+NotificationData::Button& NotificationData::Button::operator=(Button&& other) =
+    default;
+
+NotificationData::Button::~Button() = default;
+
 bool NotificationData::Button::operator==(const Button& other) const {
   return text == other.text && type == other.type && id == other.id;
 }
 
-NotificationData::Button::~Button() = default;
-
 NotificationData::NotificationData() = default;
 
 NotificationData::NotificationData(const NotificationData& other) = default;
 
+NotificationData::NotificationData(NotificationData&& other) = default;
+
+NotificationData& NotificationData::operator=(const NotificationData& other) =
+    default;
+
+NotificationData& NotificationData::operator=(NotificationData&& other) =
+    default;
+
+NotificationData::~NotificationData() = default;
+
 bool NotificationData::operator==(const NotificationData& other) const {
   return title == other.title && message == other.message &&
          custom_data == other.custom_data && buttons == other.buttons &&
          icons.size() == other.icons.size();
 }
 
-NotificationData::~NotificationData() = default;
 
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.h b/chrome/browser/notifications/scheduler/public/notification_data.h
index 5ee5cce..c9968ea 100644
--- a/chrome/browser/notifications/scheduler/public/notification_data.h
+++ b/chrome/browser/notifications/scheduler/public/notification_data.h
@@ -24,9 +24,13 @@
   struct Button {
     Button();
     Button(const Button& other);
-    bool operator==(const Button& other) const;
+    Button(Button&& other);
+    Button& operator=(const Button& other);
+    Button& operator=(Button&& other);
     ~Button();
 
+    bool operator==(const Button& other) const;
+
     // The text associated with the button.
     std::u16string text;
 
@@ -38,11 +42,16 @@
   };
 
   using CustomData = std::map<std::string, std::string>;
+
   NotificationData();
   NotificationData(const NotificationData& other);
-  bool operator==(const NotificationData& other) const;
+  NotificationData(NotificationData&& other);
+  NotificationData& operator=(const NotificationData& other);
+  NotificationData& operator=(NotificationData&& other);
   ~NotificationData();
 
+  bool operator==(const NotificationData& other) const;
+
   // The title of the notification.
   std::u16string title;
 
diff --git a/chrome/browser/notifications/scheduler/public/schedule_params.cc b/chrome/browser/notifications/scheduler/public/schedule_params.cc
index ba2597d..afaf7db 100644
--- a/chrome/browser/notifications/scheduler/public/schedule_params.cc
+++ b/chrome/browser/notifications/scheduler/public/schedule_params.cc
@@ -10,6 +10,15 @@
 
 ScheduleParams::ScheduleParams(const ScheduleParams& other) = default;
 
+ScheduleParams::ScheduleParams(ScheduleParams&& other) = default;
+
+ScheduleParams& ScheduleParams::operator=(const ScheduleParams& other) =
+    default;
+
+ScheduleParams& ScheduleParams::operator=(ScheduleParams&& other) = default;
+
+ScheduleParams::~ScheduleParams() = default;
+
 bool ScheduleParams::operator==(const ScheduleParams& other) const {
   return priority == other.priority &&
          impression_mapping == other.impression_mapping &&
@@ -18,6 +27,4 @@
          ignore_timeout_duration == other.ignore_timeout_duration;
 }
 
-ScheduleParams::~ScheduleParams() = default;
-
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/schedule_params.h b/chrome/browser/notifications/scheduler/public/schedule_params.h
index a0e7922..a51b27a 100644
--- a/chrome/browser/notifications/scheduler/public/schedule_params.h
+++ b/chrome/browser/notifications/scheduler/public/schedule_params.h
@@ -26,9 +26,13 @@
 
   ScheduleParams();
   ScheduleParams(const ScheduleParams& other);
-  bool operator==(const ScheduleParams& other) const;
+  ScheduleParams(ScheduleParams&& other);
+  ScheduleParams& operator=(const ScheduleParams& other);
+  ScheduleParams& operator=(ScheduleParams&& other);
   ~ScheduleParams();
 
+  bool operator==(const ScheduleParams& other) const;
+
   Priority priority;
 
   // Override the default mapping from an user action to impression result. By
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 478d2a9..607b7d7 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -264,6 +264,10 @@
   controller->GetEstimatedPerformanceClass(base::BindOnce(
       [](base::WeakPtr<optimization_guide::OnDeviceModelComponentStateManager>
              on_device_component_state_manager,
+         // Keep a reference to the controller to avoid it being deleted and
+         // killing the service.
+         scoped_refptr<optimization_guide::OnDeviceModelServiceController>
+             controller,
          std::optional<on_device_model::mojom::PerformanceClass>
              performance_class) {
         auto optimization_guide_performance_class =
@@ -281,7 +285,7 @@
                 optimization_guide_performance_class),
             variations::SyntheticTrialAnnotationMode::kCurrentLog);
       },
-      on_device_component_state_manager));
+      on_device_component_state_manager, controller));
 }
 
 OptimizationGuideKeyedService::OptimizationGuideKeyedService(
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
index 793c1e74..ffb95ec8 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -1159,6 +1159,9 @@
 
   histogram_tester()->ExpectTotalCount(
       "OptimizationGuide.ModelExecution.OnDeviceModelPerformanceClass", 1);
+  histogram_tester()->ExpectBucketCount(
+      "OptimizationGuide.ModelExecution.OnDeviceModelPerformanceClass",
+      OnDeviceModelPerformanceClass::kServiceCrash, 0);
 }
 
 // Creating multiple profiles isn't supported easily on ash and android.
diff --git a/chrome/browser/os_crypt/app_bound_encryption_win_browsertest.cc b/chrome/browser/os_crypt/app_bound_encryption_win_browsertest.cc
index 164d1dc..738ad939 100644
--- a/chrome/browser/os_crypt/app_bound_encryption_win_browsertest.cc
+++ b/chrome/browser/os_crypt/app_bound_encryption_win_browsertest.cc
@@ -33,6 +33,7 @@
 #include "chrome/install_static/test/scoped_install_details.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/os_crypt/async/browser/os_crypt_async.h"
+#include "components/os_crypt/sync/os_crypt.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/policy_constants.h"
@@ -144,7 +145,7 @@
 }
 
 // These tests verify that the metrics are recorded correctly. The first load of
-// browser in the PRE_ test stores the "Test Key" with app-bound encryption and
+// browser in the PRE_ test stores the "Test Key" with App-Bound encryption and
 // the second stage of the test verifies it can be retrieved successfully.
 IN_PROC_BROWSER_TEST_F(AppBoundEncryptionWinTest, PRE_MetricsTest) {
   if (!base::FeatureList::IsEnabled(
@@ -288,7 +289,7 @@
 
   void SetUp() override {
     if (!IsPreTest()) {
-      // Disable app-bound in the second part of the test.
+      // Disable App-Bound in the second part of the test.
       MaybeEnablePolicy(false);
     }
     AppBoundEncryptionWinTestWithPolicyBase::SetUp();
@@ -331,8 +332,12 @@
   // tested elsewhere.
   const auto previous_data = RetrieveData();
   ASSERT_TRUE(previous_data);
-  const auto plaintext = encryptor.DecryptData(*previous_data);
+  os_crypt_async::Encryptor::DecryptFlags flags;
+  const auto plaintext = encryptor.DecryptData(*previous_data, &flags);
   ASSERT_TRUE(plaintext);
+  // App-Bound is now disabled, so App-Bound encrypted data should be
+  // re-encrypted in order to ensure it's encrypted with the DPAPI key provider.
+  EXPECT_TRUE(flags.should_reencrypt);
   EXPECT_EQ(*plaintext, "app-bound secret");
 }
 
@@ -354,6 +359,42 @@
     ::testing::Values(
         /*policy::key::kApplicationBoundEncryptionEnabled=*/std::nullopt));
 
+class AppBoundEncryptionWinTestFeatureMaybeDisabled
+    : public AppBoundEncryptionWinTest,
+      public ::testing::WithParamInterface</*feature enabled*/ bool> {
+ public:
+  AppBoundEncryptionWinTestFeatureMaybeDisabled() {
+    feature_list_.InitWithFeatureState(
+        features::kUseAppBoundEncryptionProviderForEncryption, GetParam());
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(AppBoundEncryptionWinTestFeatureMaybeDisabled,
+                       ReEncrypt) {
+  std::string ciphertext;
+  ASSERT_TRUE(OSCrypt::EncryptString("secrets", &ciphertext));
+
+  auto encryptor = GetInstanceSync(*g_browser_process->os_crypt_async());
+
+  os_crypt_async::Encryptor::DecryptFlags flags;
+  std::string plaintext;
+  ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+  // With App-Bound enabled, a decryption of an OSCrypt Sync secret should
+  // result in a request to re-encrypt to get full protection, otherwise no
+  // re-encryption should be requested.
+  EXPECT_EQ(flags.should_reencrypt, GetParam());
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         AppBoundEncryptionWinTestFeatureMaybeDisabled,
+                         ::testing::Bool(),
+                         [](auto& info) {
+                           return info.param ? "Enabled" : "Disabled";
+                         });
+
 // These tests do not function correctly in component builds because they rely
 // on being able to run a standalone executable child process in various
 // different directories, and a component build has too many dynamic DLL
diff --git a/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator_unittest.cc b/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator_unittest.cc
index 7231909..4666c59d 100644
--- a/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator_unittest.cc
+++ b/chrome/browser/password_manager/android/built_in_backend_to_android_backend_migrator_unittest.cc
@@ -104,10 +104,10 @@
     prefs_.registry()->RegisterBooleanPref(
         syncer::prefs::internal::kSyncKeepEverythingSynced, false);
     prefs_.registry()->RegisterBooleanPref(
-        base::StrCat({syncer::prefs::internal::
-                          kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
-                      ".",
-                      syncer::GetDataTypeLowerCaseRootTag(syncer::PASSWORDS)}),
+        base::StrCat(
+            {syncer::prefs::internal::
+                 kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
+             ".", syncer::DataTypeToStableLowerCaseString(syncer::PASSWORDS)}),
         false);
     CreateMigrator(&built_in_backend_, &android_backend_, &prefs_);
   }
@@ -137,7 +137,8 @@
           base::StrCat(
               {syncer::prefs::internal::
                    kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
-               ".", syncer::GetDataTypeLowerCaseRootTag(syncer::PASSWORDS)}),
+               ".",
+               syncer::DataTypeToStableLowerCaseString(syncer::PASSWORDS)}),
           true);
     } else {
       prefs_.SetBoolean(
diff --git a/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc b/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc
index acbfc5c7..834d1bd 100644
--- a/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc
+++ b/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc
@@ -162,10 +162,10 @@
     pref_service_.registry()->RegisterBooleanPref(
         syncer::prefs::internal::kSyncKeepEverythingSynced, false);
     pref_service_.registry()->RegisterBooleanPref(
-        base::StrCat({syncer::prefs::internal::
-                          kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
-                      ".",
-                      syncer::GetDataTypeLowerCaseRootTag(syncer::PASSWORDS)}),
+        base::StrCat(
+            {syncer::prefs::internal::
+                 kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
+             ".", syncer::DataTypeToStableLowerCaseString(syncer::PASSWORDS)}),
         false);
     pref_service_.registry()->RegisterBooleanPref(
         password_manager::prefs::
@@ -196,7 +196,8 @@
           base::StrCat(
               {syncer::prefs::internal::
                    kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
-               ".", syncer::GetDataTypeLowerCaseRootTag(syncer::PASSWORDS)}),
+               ".",
+               syncer::DataTypeToStableLowerCaseString(syncer::PASSWORDS)}),
           true);
       ASSERT_EQ(browser_sync::GetSyncToSigninMigrationDataTypeDecision(
                     &pref_service_, syncer::PASSWORDS,
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
index 16650c8..ed5a9d7 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.cc
@@ -127,11 +127,6 @@
   pip_window_controller_->Show();
 
 #if !BUILDFLAG(IS_ANDROID)
-  RecordFileDialogOpenMetric(
-      number_of_open_file_dialogs_ > 0
-          ? FileDialogOpenState::kPictureInPictureOpenWithFileDialog
-          : FileDialogOpenState::kPictureInPictureOpenWithoutFileDialog);
-
   if (number_of_existing_scoped_disallow_picture_in_pictures_ > 0) {
     // Don't exit picture-in-picture synchronously since exiting in the middle
     // of opening leaves us in a bad state.
@@ -435,11 +430,6 @@
   pip_window_controller_ = video_pip_window_controller;
 
 #if !BUILDFLAG(IS_ANDROID)
-  RecordFileDialogOpenMetric(
-      number_of_open_file_dialogs_ > 0
-          ? FileDialogOpenState::kPictureInPictureOpenWithFileDialog
-          : FileDialogOpenState::kPictureInPictureOpenWithoutFileDialog);
-
   if (number_of_existing_scoped_disallow_picture_in_pictures_ > 0) {
     // Don't exit picture-in-picture synchronously since exiting in the middle
     // of opening leaves us in a bad state.
@@ -541,19 +531,6 @@
   }
 }
 
-void PictureInPictureWindowManager::OnFileDialogOpened() {
-  number_of_open_file_dialogs_++;
-  RecordFileDialogOpenMetric(
-      pip_window_controller_
-          ? FileDialogOpenState::kFileDialogOpenWithPictureInPicture
-          : FileDialogOpenState::kFileDialogOpenWithoutPictureInPicture);
-}
-
-void PictureInPictureWindowManager::OnFileDialogClosed() {
-  CHECK_NE(number_of_open_file_dialogs_, 0u);
-  number_of_open_file_dialogs_--;
-}
-
 bool PictureInPictureWindowManager::ShouldFileDialogBlockPictureInPicture(
     content::WebContents* owner_web_contents) {
   if (!base::FeatureList::IsEnabled(media::kFileDialogsBlockPictureInPicture)) {
@@ -634,12 +611,6 @@
   }
 }
 
-void PictureInPictureWindowManager::RecordFileDialogOpenMetric(
-    FileDialogOpenState state) {
-  base::UmaHistogramEnumeration("Media.PictureInPicture.FileDialogOpenState",
-                                state);
-}
-
 void PictureInPictureWindowManager::RecordPictureInPictureDisallowed(
     PictureInPictureDisallowedType type) {
   base::UmaHistogramEnumeration("Media.PictureInPicture.Disallowed", type);
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
index 3338c0a..202a8b1 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_manager.h
@@ -191,11 +191,6 @@
   // window.
   PictureInPictureOcclusionTracker* GetOcclusionTracker();
 
-  // Used for `Media.PictureInPicture.FileDialogOpenState` to determine when
-  // file dialogs and picture-in-picture windows are simultaneously open.
-  void OnFileDialogOpened();
-  void OnFileDialogClosed();
-
   // Returns true if a file dialog opened by `owner_web_contents` should create
   // a `ScopedDisallowPictureInPicture` to block picture-in-picture.
   bool ShouldFileDialogBlockPictureInPicture(
@@ -234,30 +229,6 @@
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
   //
-  // LINT.IfChange(FileDialogOpenState)
-  enum class FileDialogOpenState {
-    // A file dialog was opened while a picture-in-picture window was already
-    // open.
-    kFileDialogOpenWithPictureInPicture = 0,
-
-    // A file dialog was opened while no picture-in-picture windows were open.
-    kFileDialogOpenWithoutPictureInPicture = 1,
-
-    // A picture-in-picture window was opened while a file dialog was already
-    // open.
-    kPictureInPictureOpenWithFileDialog = 2,
-
-    // A picture-in-picture window was opened while no file dialogs were already
-    // open.
-    kPictureInPictureOpenWithoutFileDialog = 3,
-
-    kMaxValue = kPictureInPictureOpenWithoutFileDialog,
-  };
-  // LINT.ThenChange(//tools/metrics/histograms/metadata/media/enums.xml:FileDialogOpenStateEnum)
-
-  // These values are persisted to logs. Entries should not be renumbered and
-  // numeric values should never be reused.
-  //
   // LINT.IfChange(PictureInPictureDisallowedType)
   enum class PictureInPictureDisallowedType {
     // An existing picture-in-picture window was closed because we started
@@ -305,10 +276,6 @@
       const blink::mojom::PictureInPictureWindowOptions& pip_options,
       const display::Display& display);
 
-  // Records whether file dialogs and picture-in-picture windows are open at the
-  // same time.
-  void RecordFileDialogOpenMetric(FileDialogOpenState state);
-
   // Records whether a new or existing picture-in-picture window was closed due
   // to an existing ScopedDisallowPictureInPicture.
   void RecordPictureInPictureDisallowed(PictureInPictureDisallowedType type);
@@ -326,10 +293,6 @@
 
   std::unique_ptr<PictureInPictureOcclusionTracker> occlusion_tracker_;
 
-  // The number of currently open file dialogs. Used for the
-  // `Media.PictureInPicture.FileDialogOpenState` metric.
-  uint32_t number_of_open_file_dialogs_ = 0;
-
   // The number of `ScopedDisallowPictureInPicture` objects currently in
   // existence. If at least one exists, then picture-in-picture windows will be
   // blocked.
diff --git a/chrome/browser/platform_experience/win b/chrome/browser/platform_experience/win
index f6d1522..2852ae8 160000
--- a/chrome/browser/platform_experience/win
+++ b/chrome/browser/platform_experience/win
@@ -1 +1 @@
-Subproject commit f6d1522a6859c6db431fa7581179ed9342ddc457
+Subproject commit 2852ae88cdeb024e620598dce0a3aab80ab7d5b6
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc
index fa8a280..4864358 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc
@@ -11,9 +11,9 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_path_override.h"
 #include "base/test/with_feature_override.h"
-#include "base/threading/thread_restrictions.h"
 #include "base/version.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/component_updater/privacy_sandbox_attestations_component_installer_test_util.h"
@@ -27,45 +27,45 @@
 #include "content/public/test/browser_test.h"
 
 namespace privacy_sandbox {
-class PrivacySandboxAttestationsBrowserTest
+class PrivacySandboxAttestationsBrowserTestBase
     : public MixinBasedInProcessBrowserTest {
  public:
+  PrivacySandboxAttestationsBrowserTestBase() = default;
 
-  void SetUp() override {
-    MixinBasedInProcessBrowserTest::SetUp();
-    ASSERT_TRUE(DeleteInstalledComponent());
-  }
-
-  void TearDown() override {
-    MixinBasedInProcessBrowserTest::TearDown();
-    ASSERT_TRUE(DeleteInstalledComponent());
-  }
+  ~PrivacySandboxAttestationsBrowserTestBase() override = default;
 
   const base::HistogramTester& histogram_tester() const {
     return histogram_tester_;
   }
 
- protected:
-  using Installer =
-      component_updater::PrivacySandboxAttestationsComponentInstallerPolicy;
-
  private:
-  bool DeleteInstalledComponent() {
-    // Delete the privacy sandbox attestations installation directory.
-    base::FilePath component_updater_dir;
-    base::PathService::Get(component_updater::DIR_COMPONENT_USER,
-                           &component_updater_dir);
-
-    return base::DeletePathRecursively(
-        Installer::GetInstalledDirectory(component_updater_dir));
-  }
-
   PrivacySandboxAttestationsMixin privacy_sandbox_attestations_mixin_{
       &mixin_host_};
 
+  // Override the user component directories that may have the downloaded
+  // attestation file.
+  base::ScopedPathOverride user_dir_override_{
+      component_updater::DIR_COMPONENT_USER};
+
   base::HistogramTester histogram_tester_;
 };
 
+class PrivacySandboxAttestationsBrowserTest
+    : public PrivacySandboxAttestationsBrowserTestBase {
+ public:
+  PrivacySandboxAttestationsBrowserTest() = default;
+
+  ~PrivacySandboxAttestationsBrowserTest() override = default;
+
+ private:
+  // Override the pre-install component directories that may have the
+  // pre-installed attestation file.
+  base::ScopedPathOverride preinstalled_dir_override_{
+      component_updater::DIR_COMPONENT_PREINSTALLED};
+  base::ScopedPathOverride preinstalled_alt_dir_override_{
+      component_updater::DIR_COMPONENT_PREINSTALLED_ALT};
+};
+
 IN_PROC_BROWSER_TEST_F(
     PrivacySandboxAttestationsBrowserTest,
     CallComponentReadyWhenRegistrationFindsExistingComponent) {
@@ -79,10 +79,10 @@
   site_attestation.add_attested_apis(TOPICS);
   (*proto.mutable_site_attestations())[site] = site_attestation;
 
-  // There is a pre-installed attestations component. Choose a version number
-  // that is sure to be higher than the pre-installed one. This makes sure that
-  // the component installer will choose the attestations file in the user-wide
-  // component directory.
+  // There is an existing pre-installed attestations component that is shipped
+  // with Chromium. Choose a version number that is sure to be higher. This
+  // makes sure that the component installer will ignore the shipped
+  // pre-installed attestations component.
   base::Version version("12345.0.0.0");
 
   ASSERT_TRUE(
@@ -121,17 +121,6 @@
 // histogram buckets.
 IN_PROC_BROWSER_TEST_F(PrivacySandboxAttestationsBrowserTest,
                        DifferentHistogramAfterAttestationsFileCheck) {
-  // Allow blocking for file IO.
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  // Override the pre-install component directory and its alternative directory
-  // so that the component update will not find the pre-installed attestations
-  // file.
-  base::ScopedPathOverride preinstalled_dir_override(
-      component_updater::DIR_COMPONENT_PREINSTALLED);
-  base::ScopedPathOverride preinstalled_alt_dir_override(
-      component_updater::DIR_COMPONENT_PREINSTALLED_ALT);
-
   std::string site = "https://example.com";
   EXPECT_FALSE(PrivacySandboxSettingsImpl::IsAllowed(
       PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
@@ -171,34 +160,30 @@
 }
 
 class PrivacySandboxAttestationPreInstallBrowserTest
-    : public PrivacySandboxAttestationsBrowserTest,
+    : public PrivacySandboxAttestationsBrowserTestBase,
       public base::test::WithFeatureOverride {
  public:
   PrivacySandboxAttestationPreInstallBrowserTest()
       : base::test::WithFeatureOverride(
             kPrivacySandboxAttestationsLoadPreInstalledComponent) {}
+
+  ~PrivacySandboxAttestationPreInstallBrowserTest() override = default;
 };
 
 // If there is no attestation list in user directory and feature
 // "PrivacySandboxAttestationsLoadPreInstalledComponent" is enabled, the
-// pre-installed version should be used.
+// pre-installed version should be used. This test verifies there is a
+// pre-installed attestation list shipped with Chromium.
 IN_PROC_BROWSER_TEST_P(PrivacySandboxAttestationPreInstallBrowserTest,
                        PreinstalledAttestationListPresent) {
-  // Allow blocking for file IO.
-  base::ScopedAllowBlockingForTesting allow_blocking;
-
-  // Override the user-wide component directory to make sure there is no
-  // downloaded attestation list.
-  base::ScopedPathOverride user_dir_override(
-      component_updater::DIR_COMPONENT_USER);
-
   base::RunLoop run_loop;
   PrivacySandboxAttestations::GetInstance()
       ->SetLoadAttestationsDoneCallbackForTesting(run_loop.QuitClosure());
 
   // Register the privacy sandbox attestations component, which should parse
   // the pre-installed attestations file on disk if feature
-  // "PrivacySandboxAttestationsLoadPreInstalledComponent" is enabled.
+  // "PrivacySandboxAttestationsLoadPreInstalledComponent" is enabled. This
+  // pre-installed file is shipped with Chromium.
   RegisterPrivacySandboxAttestationsComponent(
       g_browser_process->component_updater());
 
@@ -220,8 +205,12 @@
                                          FileSource::kPreInstalled, 1);
   } else {
     // If feature off, the attestation component should not parse the
-    // pre-installed file.
-    run_loop.RunUntilIdle();
+    // pre-installed file. Since there is no downloaded attestation file, the
+    // attestations component ends up with no attestation map.
+    ASSERT_TRUE(base::test::RunUntil([]() {
+      return PrivacySandboxAttestations::GetInstance()
+          ->attestations_file_checked();
+    }));
 
     ASSERT_FALSE(PrivacySandboxAttestations::GetInstance()
                      ->GetVersionForTesting()
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 ac64a2f..bbc7170f 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
@@ -1033,7 +1033,7 @@
      * highlighting.
      */
     public boolean timepointsSupported(Tab tab) {
-        if (isAvailable() && !GURL.isEmptyOrInvalid(tab.getUrl())) {
+        if (!GURL.isEmptyOrInvalid(tab.getUrl())) {
             int urlHash = urlToHash(stripUserData(tab.getUrl()).getSpec());
             if (sReadabilityInfoMap.get(urlHash) == null) {
                 return false;
diff --git a/chrome/browser/resources/ash/settings/device_page/audio.html b/chrome/browser/resources/ash/settings/device_page/audio.html
index 327faf8..fb7b7c7 100644
--- a/chrome/browser/resources/ash/settings/device_page/audio.html
+++ b/chrome/browser/resources/ash/settings/device_page/audio.html
@@ -197,7 +197,6 @@
       <div id="audioInputStyleTransferLabel"
           class="settings-box-text start" aria-hidden="true">
         $i18n{audioInputStyleTransferTitle}
-        <cros-badge>$i18n{audioInputStyleTransferBadge}</cros-badge>
         <div class="secondary">
           $i18n{audioInputStyleTransferDescription}
         </div>
diff --git a/chrome/browser/resources/ash/settings/device_page/audio.ts b/chrome/browser/resources/ash/settings/device_page/audio.ts
index 0c8cb1a..6a3949717 100644
--- a/chrome/browser/resources/ash/settings/device_page/audio.ts
+++ b/chrome/browser/resources/ash/settings/device_page/audio.ts
@@ -8,12 +8,11 @@
  * settings.
  */
 
-import 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
-import 'chrome://resources/ash/common/cr_elements/policy/cr_policy_indicator.js';
-import 'chrome://resources/ash/common/cr_elements/cr_slider/cr_slider.js';
-import 'chrome://resources/cros_components/badge/badge.js'; // side-effect
 import '../icons.html.js';
 import '../settings_shared.css.js';
+import 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
+import 'chrome://resources/ash/common/cr_elements/cr_slider/cr_slider.js';
+import 'chrome://resources/ash/common/cr_elements/policy/cr_policy_indicator.js';
 
 import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';
 import {CrSliderElement} from 'chrome://resources/ash/common/cr_elements/cr_slider/cr_slider.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test.js
index 513692e..f5703b2 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test.js
@@ -1419,3 +1419,33 @@
   assertEquals(keyEvents[1].keyCode, KeyCode.C);
   assertObjectEquals(keyEvents[1].modifiers, {ctrl: true});
 });
+
+AX_TEST_F('FaceGazeTest', 'VelocityThreshold', async function() {
+  const config = new Config()
+                     .withMouseLocation({x: 600, y: 400})
+                     .withBufferSize(1)
+                     .withCursorControlEnabled(true)
+                     .withVelocityThreshold()
+                     .withSpeeds(1, 1, 1, 1);
+  await this.startFacegazeWithConfigAndForeheadLocation_(config, 0.1, 0.2);
+  assertNullOrUndefined(
+      this.mockAccessibilityPrivate.getLatestCursorPosition());
+
+  // Manually set the velocity threshold to 1. This means that the mouse needs
+  // to move by more than one pixel before it will actually be moved.
+  this.getFaceGaze().mouseController_.velocityThreshold_ = 1;
+
+  // Small movement in head location (e.g. one pixel) doesn't trigger any
+  // mouse movement.
+  result = new MockFaceLandmarkerResult().setNormalizedForeheadLocation(
+      0.101, 0.201);
+  this.processFaceLandmarkerResult(result);
+  assertNullOrUndefined(
+      this.mockAccessibilityPrivate.getLatestCursorPosition());
+
+  // Large movement triggers mouse movement.
+  result =
+      new MockFaceLandmarkerResult().setNormalizedForeheadLocation(0.11, 0.21);
+  this.processFaceLandmarkerResult(result);
+  this.assertLatestCursorPosition({x: 590, y: 406});
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test_base.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test_base.js
index a5467975..3d836d0 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test_base.js
@@ -18,6 +18,10 @@
     this.bufferSize = -1;
     /** @type {boolean} */
     this.useMouseAcceleration = false;
+    /** @type {boolean} */
+    this.useLandmarkWeights = false;
+    /** @type {boolean} */
+    this.useVelocityThreshold = false;
     /** @type {?Map<string, number>} */
     this.speeds = null;
     /** @type {number} */
@@ -64,14 +68,24 @@
     return this;
   }
 
-  /**
-   * @return {!Config}
-   */
+  /** @return {!Config} */
   withMouseAcceleration() {
     this.useMouseAcceleration = true;
     return this;
   }
 
+  /** @return {!Config} */
+  withLandmarkWeights() {
+    this.useLandmarkWeights = true;
+    return this;
+  }
+
+  /** @return {!Config} */
+  withVelocityThreshold() {
+    this.useVelocityThreshold = true;
+    return this;
+  }
+
   /**
    * @param {number} up
    * @param {number} down
@@ -86,6 +100,7 @@
 
   /**
    * @param {number} repeatDelayMs
+   * @return {!Config}
    */
   withRepeatDelayMs(repeatDelayMs) {
     this.repeatDelayMs = repeatDelayMs;
@@ -94,6 +109,7 @@
 
   /**
    * @param {boolean} cursorControlEnabled
+   * @return {!Config}
    */
   withCursorControlEnabled(cursorControlEnabled) {
     this.cursorControlEnabled = cursorControlEnabled;
@@ -102,6 +118,7 @@
 
   /**
    * @param {boolean} actionsEnabled
+   * @return {!Config}
    */
   withActionsEnabled(actionsEnabled) {
     this.actionsEnabled = actionsEnabled;
@@ -242,18 +259,18 @@
   async startFacegazeWithConfigAndForeheadLocation_(
       config, forehead_x, forehead_y) {
     await this.configureFaceGaze(config);
-
     // No matter the starting location, the cursor position won't change
     // initially, and upcoming forehead locations will be computed relative to
     // this.
     const result = new MockFaceLandmarkerResult().setNormalizedForeheadLocation(
         forehead_x, forehead_y);
     this.processFaceLandmarkerResult(result);
-    if (config.cursorControlEnabled) {
+    if (config.cursorControlEnabled && !config.useVelocityThreshold) {
       this.assertLatestCursorPosition(config.mouseLocation);
     } else {
       assertEquals(
-          null, this.mockAccessibilityPrivate.getLatestCursorPosition());
+          null, this.mockAccessibilityPrivate.getLatestCursorPosition(),
+          'Expected cursor position to be null');
     }
   }
 
@@ -300,6 +317,12 @@
       faceGaze.gestureHandler_.repeatDelayMs_ = config.repeatDelayMs;
     }
 
+    faceGaze.mouseController_.setLandmarkWeightsForTesting(
+        config.useLandmarkWeights);
+
+    faceGaze.mouseController_.setVelocityThresholdForTesting(
+        config.useVelocityThreshold);
+
     await this.setPref(
         MouseController.PREF_CURSOR_USE_ACCELERATION,
         config.useMouseAcceleration);
@@ -374,8 +397,12 @@
   /** @param {!{x: number, y: number}} expected */
   assertLatestCursorPosition(expected) {
     const actual = this.mockAccessibilityPrivate.getLatestCursorPosition();
-    assertEquals(expected.x, actual.x);
-    assertEquals(expected.y, actual.y);
+    assertEquals(
+        expected.x, actual.x,
+        'Failed to assert latest cursor position x value');
+    assertEquals(
+        expected.y, actual.y,
+        'Failed to assert latest cursor position y value');
   }
 
   /** @param {number} num */
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test_support.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test_support.js
index 739891e..0fff3d6d 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test_support.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze_test_support.js
@@ -66,6 +66,18 @@
     this.notifyCcTests_();
   }
 
+  /** @param {boolean} useWeights */
+  setLandmarkWeights(useWeights) {
+    this.getMouseController_().setLandmarkWeightsForTesting(useWeights);
+    this.notifyCcTests_();
+  }
+
+  /** @param {boolean} useThreshold */
+  setVelocityThreshold(useThreshold) {
+    this.getMouseController_().setVelocityThresholdForTesting(useThreshold);
+    this.notifyCcTests_();
+  }
+
   /** Instantiates the FaceLandmarker. */
   async createFaceLandmarker() {
     const webCamFaceLandmarker = this.getWebCamFaceLandmarker_();
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/mouse_controller.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/mouse_controller.ts
index 079ae936..0077b6c6 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/mouse_controller.ts
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/mouse_controller.ts
@@ -27,6 +27,15 @@
   y: number;
 }
 
+enum LandmarkType {
+  FOREHEAD = 'forehead',
+  FOREHEAD_TOP = 'foreheadTop',
+  LEFT_TEMPLE = 'leftTemple',
+  NOSE_TIP = 'noseTip',
+  RIGHT_TEMPLE = 'rightTemple',
+  ROTATION = 'rotation',
+}
+
 /** Handles all interaction with the mouse. */
 export class MouseController {
   /** Last seen mouse location (cached from event in onMouseMovedOrDragged_). */
@@ -45,6 +54,8 @@
   private spdLeft_ = MouseController.DEFAULT_MOUSE_SPEED;
   private spdUp_ = MouseController.DEFAULT_MOUSE_SPEED;
   private spdDown_ = MouseController.DEFAULT_MOUSE_SPEED;
+  private velocityThreshold_ = 0;
+  private useVelocityThreshold_ = true;
 
   /** The most recent raw face landmark mouse locations. */
   private buffer_: ScreenPoint[] = [];
@@ -87,10 +98,15 @@
     this.scrollModeController_ = new ScrollModeController();
 
     this.calcSmoothKernel_();
+    this.calcVelocityThreshold_();
+
     this.landmarkWeights_ = new Map();
-    // TODO(b:309121742): This should be a fixed list of weights depending on
-    // what works best from experimentation.
-    this.landmarkWeights_.set('forehead', 1);
+    this.landmarkWeights_.set(LandmarkType.FOREHEAD, 0.1275);
+    this.landmarkWeights_.set(LandmarkType.FOREHEAD_TOP, 0.0738);
+    this.landmarkWeights_.set(LandmarkType.NOSE_TIP, 0.3355);
+    this.landmarkWeights_.set(LandmarkType.LEFT_TEMPLE, 0.0336);
+    this.landmarkWeights_.set(LandmarkType.RIGHT_TEMPLE, 0.0336);
+    this.landmarkWeights_.set(LandmarkType.ROTATION, 0.3960);
 
     this.prefsListener_ = prefs => this.updateFromPrefs_(prefs);
     this.init();
@@ -153,9 +169,7 @@
     this.landmarkWeights_ = weights;
   }
 
-  /**
-   * Update the current location of the tracked face landmark.
-   */
+  /** Update the current location of the tracked face landmark. */
   onFaceLandmarkerResult(result: FaceLandmarkerResult): void {
     if (this.paused_ || !this.screenBounds_ || !result.faceLandmarks ||
         !result.faceLandmarks[0]) {
@@ -240,16 +254,26 @@
       // start-up.
       this.previousSmoothedLocation_ = smoothed;
     }
+
     const velocityX = smoothed.x - this.previousSmoothedLocation_.x;
     const velocityY = smoothed.y - this.previousSmoothedLocation_.y;
     const scaledVel = this.asymmetryScale_({x: velocityX, y: velocityY});
     this.previousSmoothedLocation_ = smoothed;
-
     if (this.useMouseAcceleration_) {
       scaledVel.x *= this.applySigmoidAcceleration_(scaledVel.x);
       scaledVel.y *= this.applySigmoidAcceleration_(scaledVel.y);
     }
 
+    if (!this.exceedsVelocityThreshold_(scaledVel.x) &&
+        !this.exceedsVelocityThreshold_(scaledVel.y)) {
+      // The velocity threshold wasn't exceeded, so we shouldn't update the
+      // mouse location. We do this to avoid unintended jitteriness of the
+      // mouse.
+      return;
+    }
+
+    scaledVel.x = this.applyVelocityThreshold_(scaledVel.x);
+    scaledVel.y = this.applyVelocityThreshold_(scaledVel.y);
     // The mouse location is the previous location plus the velocity.
     const newX = this.mouseLocation_.x + scaledVel.x;
     const newY = this.mouseLocation_.y + scaledVel.y;
@@ -598,21 +622,25 @@
         case MouseController.PREF_SPD_UP:
           if (pref.value) {
             this.spdUp_ = pref.value;
+            this.calcVelocityThreshold_();
           }
           break;
         case MouseController.PREF_SPD_DOWN:
           if (pref.value) {
             this.spdDown_ = pref.value;
+            this.calcVelocityThreshold_();
           }
           break;
         case MouseController.PREF_SPD_LEFT:
           if (pref.value) {
             this.spdLeft_ = pref.value;
+            this.calcVelocityThreshold_();
           }
           break;
         case MouseController.PREF_SPD_RIGHT:
           if (pref.value) {
             this.spdRight_ = pref.value;
+            this.calcVelocityThreshold_();
           }
           break;
         case MouseController.PREF_CURSOR_SMOOTHING:
@@ -634,6 +662,48 @@
       }
     });
   }
+
+  private calcVelocityThreshold_(): void {
+    // Threshold is a function of speed. Threshold increases as speed increases
+    // because it's easier to move the mouse accidentally at high mouse speeds.
+    const averageSpeed =
+        (this.spdUp_ + this.spdDown_ + this.spdLeft_ + this.spdRight_) / 4;
+    this.velocityThreshold_ = averageSpeed * 0.3;
+  }
+
+  private exceedsVelocityThreshold_(velocity: number): boolean {
+    if (!this.useVelocityThreshold_) {
+      return true;
+    }
+
+    return Math.abs(velocity) > this.velocityThreshold_;
+  }
+
+  private applyVelocityThreshold_(velocity: number): number {
+    if (!this.useVelocityThreshold_) {
+      return velocity;
+    }
+
+    if (Math.abs(velocity) < this.velocityThreshold_) {
+      return 0;
+    }
+
+    return (velocity > 0) ? velocity - this.velocityThreshold_ :
+                            velocity + this.velocityThreshold_;
+  }
+
+  setLandmarkWeightsForTesting(useWeights: boolean): void {
+    if (!useWeights) {
+      // If we don't want to use landmark weights, we should default to the
+      // forehead location.
+      this.landmarkWeights_ = new Map();
+      this.landmarkWeights_.set('forehead', 1);
+    }
+  }
+
+  setVelocityThresholdForTesting(useThreshold: boolean): void {
+    this.useVelocityThreshold_ = useThreshold;
+  }
 }
 
 export namespace MouseController {
@@ -643,14 +713,14 @@
    * https://storage.googleapis.com/mediapipe-assets/documentation/mediapipe_face_landmark_fullsize.png.
    */
   export const LANDMARK_INDICES = [
-    {name: 'forehead', index: 8},
-    {name: 'foreheadTop', index: 10},
-    {name: 'noseTip', index: 4},
-    {name: 'rightTemple', index: 127},
-    {name: 'leftTemple', index: 356},
+    {name: LandmarkType.FOREHEAD, index: 8},
+    {name: LandmarkType.FOREHEAD_TOP, index: 10},
+    {name: LandmarkType.NOSE_TIP, index: 4},
+    {name: LandmarkType.RIGHT_TEMPLE, index: 127},
+    {name: LandmarkType.LEFT_TEMPLE, index: 356},
     // Rotation does not have a landmark index, but is included in this list
     // because it can be used as a landmark.
-    {name: 'rotation', index: -1},
+    {name: LandmarkType.ROTATION, index: -1},
   ];
 
   /** How frequently to run the mouse movement logic. */
diff --git a/chrome/browser/resources/chromeos/accessibility/common/action_fulfillment/macros/key_press_macro.ts b/chrome/browser/resources/chromeos/accessibility/common/action_fulfillment/macros/key_press_macro.ts
index 10cc0b8..45d597d 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/action_fulfillment/macros/key_press_macro.ts
+++ b/chrome/browser/resources/chromeos/accessibility/common/action_fulfillment/macros/key_press_macro.ts
@@ -34,9 +34,9 @@
     const key = this.keyCombination_.key;
     const modifiers = this.keyCombination_.modifiers || {};
     if (this.runCount_ === 0) {
-      EventGenerator.sendKeyDown(key, modifiers);
+      EventGenerator.sendKeyDown(key, modifiers, /*use_rewriters=*/ true);
     } else if (this.runCount_ === 1) {
-      EventGenerator.sendKeyUp(key, modifiers);
+      EventGenerator.sendKeyUp(key, modifiers, /*use_rewriters=*/ true);
     } else {
       console.error('Key press macro cannot be run more than twice.');
       return this.createRunMacroResult_(
diff --git a/chrome/browser/resources/commerce/product_specifications/horizontal_carousel.css b/chrome/browser/resources/commerce/product_specifications/horizontal_carousel.css
index 43f7bee..fa1a1c1 100644
--- a/chrome/browser/resources/commerce/product_specifications/horizontal_carousel.css
+++ b/chrome/browser/resources/commerce/product_specifications/horizontal_carousel.css
@@ -10,74 +10,80 @@
  * #include=cr-hidden-style-lit cr-icons-lit
  * #css_wrapper_metadata_end */
 
- :host {
-    --horizontal-carousel-button-size: 32px;
+:host {
+  --horizontal-carousel-button-size: 32px;
 
-    display: flex;
-    isolation: isolate;
-    position: relative;
-  }
+  display: flex;
+  isolation: isolate;
+  position: relative;
+}
 
-  .carousel-button,
-  .hover-layer {
-    border-radius: 50%;
-    position: absolute;
-    top: 44%;
-    transform: translateY(-50%);
-  }
+.carousel-button,
+.hover-layer {
+  border-radius: 50%;
+  position: absolute;
+}
 
-  .carousel-button {
-    --cr-icon-button-size: var(--horizontal-carousel-button-size);
-    background-color: var(--color-button-background-tonal);
-    margin: 0;
-    /* Buttons should display on top of any overflowing text. */
-    z-index: 1;
-  }
+.carousel-button {
+  --cr-icon-button-size: var(--horizontal-carousel-button-size);
+  background-color: var(--color-button-background-tonal);
+  margin: 0;
+}
 
-  .hover-layer {
-    background: var(--cr-hover-background-color);
-    display: none;
-    height: var(--horizontal-carousel-button-size);
-    width: var(--horizontal-carousel-button-size);
-    pointer-events: none;
-    z-index: 2;
-  }
+.carousel-button-container {
+  height: 0;
+  position: sticky;
+  top: min(44vh, 44%);
+  width: 0;
+  z-index: 1; /* Buttons should display on top of any overflowing text. */
+}
 
-  #backButton, #backHoverLayer {
-    left: calc(-1 * var(--horizontal-carousel-button-size));
-  }
+.hover-layer {
+  background: var(--cr-hover-background-color);
+  display: none;
+  height: var(--horizontal-carousel-button-size);
+  left: 0;
+  pointer-events: none;
+  position: absolute;
+  width: var(--horizontal-carousel-button-size);
+  z-index: 1;
+}
 
-  #forwardButton, #forwardHoverLayer {
-    right: calc(-1 * var(--horizontal-carousel-button-size));
-  }
+#backButton, #backHoverLayer {
+  left: calc(-1 * var(--horizontal-carousel-button-size));
+}
 
-  #backButton:hover ~ #backHoverLayer,
-  #forwardButton:hover ~ #forwardHoverLayer {
-    display: block;
-  }
+#forwardButton, #forwardHoverLayer {
+  right: calc(-1 * var(--horizontal-carousel-button-size));
+}
 
-  #carouselContainer {
-    display: flex;
-    flex-wrap: nowrap;
-    min-width: 0;
-    max-width: 1140px;
-    overflow-x: auto;
-    scroll-behavior: smooth;
-    scroll-snap-type: x mandatory;
-  }
+#backButton:hover~#backHoverLayer,
+#forwardButton:hover~#forwardHoverLayer {
+  display: block;
+}
 
-  :host(:not([can-scroll_])) #carouselContainer {
-    scrollbar-width: none;
-  }
+#carouselContainer {
+  display: flex;
+  flex-wrap: nowrap;
+  min-width: 0;
+  max-width: 1140px;
+  overflow-x: auto;
+  scroll-behavior: smooth;
+  scroll-snap-type: x mandatory;
+}
 
-  :host([can-scroll_]) #carouselContainer::-webkit-scrollbar {
-    background: transparent;
-    height: 6px;
-    width: 0;
-  }
+:host(:not([can-scroll_])) #carouselContainer {
+  scrollbar-width: none;
+}
 
-  :host([can-scroll_]) #carouselContainer::-webkit-scrollbar-thumb {
-    background:
-        var(--color-product-specifications-horizontal-carousel-scrollbar-thumb);
-    border-radius: 8px;
-  }
+:host([can-scroll_]) #carouselContainer::-webkit-scrollbar {
+  background: transparent;
+  height: 6px;
+  width: 0;
+}
+
+:host([can-scroll_]) #carouselContainer::-webkit-scrollbar-thumb {
+  background:
+    var(--color-product-specifications-horizontal-carousel-scrollbar-thumb);
+  border-radius: 8px;
+}
diff --git a/chrome/browser/resources/commerce/product_specifications/horizontal_carousel.html.ts b/chrome/browser/resources/commerce/product_specifications/horizontal_carousel.html.ts
index 055e77b..a65fcef0 100644
--- a/chrome/browser/resources/commerce/product_specifications/horizontal_carousel.html.ts
+++ b/chrome/browser/resources/commerce/product_specifications/horizontal_carousel.html.ts
@@ -8,21 +8,25 @@
 
 export function getHtml(this: HorizontalCarouselElement) {
   return html`
-  <cr-icon-button id="backButton" class="carousel-button"
-      @click="${this.onCarouselBackClick_}" iron-icon="cr:chevron-left"
-      ?hidden="${!this.showBackButton_}" tabindex="-1">
-  </cr-icon-button>
-  <div id="backHoverLayer" class="hover-layer"></div>
-
-  <cr-icon-button id="forwardButton" class="carousel-button"
-      @click="${this.onCarouselForwardClick_}" iron-icon="cr:chevron-right"
-      ?hidden="${!this.showForwardButton}" tabindex="-1">
-  </cr-icon-button>
-  <div id="forwardHoverLayer" class="hover-layer"></div>
+  <div class="carousel-button-container">
+    <cr-icon-button id="backButton" class="carousel-button"
+        @click="${this.onCarouselBackClick_}" iron-icon="cr:chevron-left"
+        ?hidden="${!this.showBackButton_}" tabindex="-1">
+    </cr-icon-button>
+    <div id="backHoverLayer" class="hover-layer"></div>
+  </div>
 
   <div id="carouselContainer">
     <div id="startProbe"></div>
     <slot name="table" id="slottedTable"></slot>
     <div id="endProbe"></div>
+  </div>
+
+  <div class="carousel-button-container">
+    <cr-icon-button id="forwardButton" class="carousel-button"
+        @click="${this.onCarouselForwardClick_}" iron-icon="cr:chevron-right"
+        ?hidden="${!this.showForwardButton}" tabindex="-1">
+    </cr-icon-button>
+    <div id="forwardHoverLayer" class="hover-layer"></div>
   </div>`;
 }
diff --git a/chrome/browser/resources/downloads/BUILD.gn b/chrome/browser/resources/downloads/BUILD.gn
index 6bcb1bb..9e9169d 100644
--- a/chrome/browser/resources/downloads/BUILD.gn
+++ b/chrome/browser/resources/downloads/BUILD.gn
@@ -15,9 +15,6 @@
     "images/no_downloads.svg",
   ]
 
-  # Files holding a Lit element definition and have an equivalent .html file.
-  web_component_files = [ "item.ts" ]
-
   non_web_component_files = [
     "browser_proxy.ts",
     "bypass_warning_confirmation_dialog.html.ts",
@@ -27,6 +24,8 @@
     "data.ts",
     "downloads.ts",
     "icon_loader.ts",
+    "item.html.ts",
+    "item.ts",
     "manager.html.ts",
     "manager.ts",
     "search_service.ts",
@@ -60,13 +59,17 @@
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   ts_deps = [
     "//third_party/lit/v3_0:build_ts",
-    "//third_party/polymer/v3_0:library",
     "//ui/webui/resources/cr_components/managed_footnote:build_ts",
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
 
+  # TODO: Remove this once internal/icons.html has been migrated to Lit.
+  if (is_chrome_branded) {
+    ts_deps += [ "//third_party/polymer/v3_0:library" ]
+  }
+
   webui_context_type = "trusted"
   optimize = optimize_webui
   if (optimize) {
diff --git a/chrome/browser/resources/downloads/downloads.ts b/chrome/browser/resources/downloads/downloads.ts
index 42bc42a..f4036a34 100644
--- a/chrome/browser/resources/downloads/downloads.ts
+++ b/chrome/browser/resources/downloads/downloads.ts
@@ -12,7 +12,7 @@
 export {MojomData} from './data.js';
 export {DangerType, PageCallbackRouter, PageHandlerInterface, PageRemote, SafeBrowsingState, State, TailoredWarningType} from './downloads.mojom-webui.js';
 export {IconLoader, IconLoaderImpl} from './icon_loader.js';
-export {ItemElement} from './item.js';
+export {DownloadsItemElement} from './item.js';
 export {DownloadsManagerElement} from './manager.js';
 export {SearchService} from './search_service.js';
 export {DownloadsToolbarElement} from './toolbar.js';
diff --git a/chrome/browser/resources/downloads/icons.html b/chrome/browser/resources/downloads/icons.html
index 1a60b52..f8132a4 100644
--- a/chrome/browser/resources/downloads/icons.html
+++ b/chrome/browser/resources/downloads/icons.html
@@ -1,4 +1,4 @@
-<iron-iconset-svg name="downloads" size="24">
+<cr-iconset name="downloads" size="24">
   <svg>
     <defs>
       <!--
@@ -20,4 +20,4 @@
       </g>
     </defs>
   </svg>
-</iron-iconset-svg>
+</cr-iconset>
diff --git a/chrome/browser/resources/downloads/item.html b/chrome/browser/resources/downloads/item.html.ts
similarity index 94%
rename from chrome/browser/resources/downloads/item.html
rename to chrome/browser/resources/downloads/item.html.ts
index 56650929..1e0f2c8 100644
--- a/chrome/browser/resources/downloads/item.html
+++ b/chrome/browser/resources/downloads/item.html.ts
@@ -1,3 +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.
+
+import {html} from '//resources/lit/v3_0/lit.rollup.js';
+
+import type {DownloadsItemElement} from './item.js';
+
+export function getHtml(this: DownloadsItemElement) {
+  // clang-format off
+  return html`<!--_html_template_start_-->
 <div id="date" role="heading" aria-level="2">${this.computeDate_()}</div>
 
 <div id="content" @dragstart="${this.onDragStart_}"
@@ -174,3 +185,6 @@
       </cr-link-row>` : ''}
   </if>
 </div>
+<!--_html_template_end_-->`;
+  // clang-format on
+}
diff --git a/chrome/browser/resources/downloads/item.ts b/chrome/browser/resources/downloads/item.ts
index 1c75213..5048b97 100644
--- a/chrome/browser/resources/downloads/item.ts
+++ b/chrome/browser/resources/downloads/item.ts
@@ -40,9 +40,7 @@
 import {getCss} from './item.css.js';
 import {getHtml} from './item.html.js';
 
-// TODO (rbpotter): Rename this back to DownloadsItemElement when .html.ts file
-// is checked in.
-export interface ItemElement {
+export interface DownloadsItemElement {
   $: {
     'controlled-by': HTMLElement,
     'file-icon': HTMLImageElement,
@@ -52,7 +50,7 @@
   };
 }
 
-const ItemElementBase = I18nMixinLit(FocusRowMixinLit(CrLitElement));
+const DownloadsItemElementBase = I18nMixinLit(FocusRowMixinLit(CrLitElement));
 
 /**
  * The UI pattern for displaying a download. Computed from DangerType and other
@@ -67,7 +65,7 @@
   ERROR,
 }
 
-export class ItemElement extends ItemElementBase {
+export class DownloadsItemElement extends DownloadsItemElementBase {
   static get is() {
     return 'downloads-item';
   }
@@ -1155,11 +1153,6 @@
     this.getMoreActionsMenu().close();
   }
 
-  private onOpenNowClick_() {
-    this.mojoHandler_!.openDuringScanningRequiringGesture(this.dataId_());
-    this.getMoreActionsMenu().close();
-  }
-
   private dataId_(): string {
     return this.data ? this.data.id : '';
   }
@@ -1333,8 +1326,8 @@
 
 declare global {
   interface HTMLElementTagNameMap {
-    'downloads-item': ItemElement;
+    'downloads-item': DownloadsItemElement;
   }
 }
 
-customElements.define(ItemElement.is, ItemElement);
+customElements.define(DownloadsItemElement.is, DownloadsItemElement);
diff --git a/chrome/browser/resources/downloads/manager.ts b/chrome/browser/resources/downloads/manager.ts
index 0429197..28c0c2a 100644
--- a/chrome/browser/resources/downloads/manager.ts
+++ b/chrome/browser/resources/downloads/manager.ts
@@ -97,7 +97,8 @@
 
   protected items_: MojomData[] = [];
   protected hasDownloads_: boolean = false;
-  private hasShadow_: boolean = false;
+  // Used for CSS styling.
+  protected hasShadow_: boolean = false;
   protected inSearchMode_: boolean = false;
   protected spinnerActive_: boolean = false;
   protected bypassPromptItemId_: string = '';
diff --git a/chrome/browser/resources/downloads/toolbar.css b/chrome/browser/resources/downloads/toolbar.css
index c870b6f..4621efec 100644
--- a/chrome/browser/resources/downloads/toolbar.css
+++ b/chrome/browser/resources/downloads/toolbar.css
@@ -4,7 +4,9 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
+ * #import=//resources/cr_elements/cr_hidden_style_lit.css.js
  * #scheme=relative
+ * #include=cr-hidden-style-lit
  * #css_wrapper_metadata_end */
 
 :host {
diff --git a/chrome/browser/resources/downloads/toolbar.ts b/chrome/browser/resources/downloads/toolbar.ts
index f3d20d2e..0c9a4dea 100644
--- a/chrome/browser/resources/downloads/toolbar.ts
+++ b/chrome/browser/resources/downloads/toolbar.ts
@@ -4,7 +4,6 @@
 
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
-import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
 import 'chrome://resources/cr_elements/icons_lit.html.js';
 import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
 import 'chrome://resources/js/util.js';
diff --git a/chrome/browser/resources/lens/overlay/BUILD.gn b/chrome/browser/resources/lens/overlay/BUILD.gn
index 4fa4014..4c28b1f 100644
--- a/chrome/browser/resources/lens/overlay/BUILD.gn
+++ b/chrome/browser/resources/lens/overlay/BUILD.gn
@@ -12,6 +12,7 @@
     out_grd = "$target_gen_dir/icon_resources.grdp"
     input_files = [
       "copy.svg",
+      "download.svg",
       "lens.svg",
       "search.svg",
       "stars.svg",
diff --git a/chrome/browser/resources/lens/overlay/lens_overlay_app.html b/chrome/browser/resources/lens/overlay/lens_overlay_app.html
index 5bc5c887..314a84d 100644
--- a/chrome/browser/resources/lens/overlay/lens_overlay_app.html
+++ b/chrome/browser/resources/lens/overlay/lens_overlay_app.html
@@ -61,7 +61,7 @@
     display: none;
   }
 
-  #copyToast {
+  #toast {
     justify-content: space-between;
   }
 
@@ -170,7 +170,6 @@
     on-screenshot-rendered="onScreenshotRendered"
     on-selection-overlay-clicked="handleSelectionOverlayClicked"
     on-pointer-released="handlePointerReleased"
-    on-text-copied="showTextCopiedToast"
     on-pointerenter="handlePointerEnter"
     on-pointerleave="handlePointerLeave"
     style="
@@ -187,8 +186,8 @@
     --color-selection-element: [[skColorToHex_(theme.selectionElement)]];
     --color-overlay-icon: white;
     --color-overlay-button-label: white;">
-  <cr-toast id="copyToast" duration="4000">
-    <div>$i18n{copyToastMessage}</div>
+  <cr-toast id="toast" duration="4000">
+    <div>[[toastMessage]]</div>
     <cr-button on-click="onHideToastClick">
       $i18n{dismiss}
     </cr-button>
diff --git a/chrome/browser/resources/lens/overlay/lens_overlay_app.ts b/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
index c8d74a9..461035c9 100644
--- a/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
+++ b/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
@@ -11,6 +11,7 @@
 
 import type {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import type {CrToastElement} from '//resources/cr_elements/cr_toast/cr_toast.js';
+import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js';
 import {assert} from '//resources/js/assert.js';
 import {skColorToHexColor} from '//resources/js/color_utils.js';
 import {EventTracker} from '//resources/js/event_tracker.js';
@@ -36,16 +37,18 @@
   $: {
     backgroundScrim: HTMLElement,
     closeButton: CrIconButtonElement,
-    copyToast: CrToastElement,
     cursorTooltip: CursorTooltipElement,
     initialGradient: InitialGradientElement,
     moreOptionsButton: CrIconButtonElement,
     moreOptionsMenu: HTMLElement,
     selectionOverlay: SelectionOverlayElement,
+    toast: CrToastElement,
   };
 }
 
-export class LensOverlayAppElement extends PolymerElement {
+const LensOverlayAppElementBase = I18nMixin(PolymerElement);
+
+export class LensOverlayAppElement extends LensOverlayAppElementBase {
   static get is() {
     return 'lens-overlay-app';
   }
@@ -91,6 +94,7 @@
         type: Object,
         value: getFallbackTheme,
       },
+      toastMessage: String,
     };
   }
 
@@ -117,6 +121,7 @@
   private shouldFadeOutButtons: boolean = false;
   // The overlay theme.
   private theme: OverlayTheme;
+  private toastMessage: string = '';
 
   private eventTracker_: EventTracker = new EventTracker();
 
@@ -161,6 +166,12 @@
         document, 'text-selection-state-changed', (e: CustomEvent) => {
           this.isTextLayerHighlightingText = e.detail.highlightingText;
         });
+    this.eventTracker_.add(document, 'text-copied', () => {
+      this.showToast(this.i18n('copyToastMessage'));
+    });
+    this.eventTracker_.add(document, 'copied-as-image', () => {
+      this.showToast(this.i18n('copyAsImageToastMessage'));
+    });
   }
 
   override disconnectedCallback() {
@@ -289,21 +300,23 @@
         (this.isTextLayerHighlightingText || this.isPointerDown);
   }
 
-  private async showTextCopiedToast() {
-    if (this.$.copyToast.open) {
+  private async showToast(message: string) {
+    if (this.$.toast.open) {
       // If toast already open, wait after hiding so that animation is
       // smoother.
-      await this.$.copyToast.hide();
+      await this.$.toast.hide();
       setTimeout(() => {
-        this.$.copyToast.show();
+        this.toastMessage = message;
+        this.$.toast.show();
       }, 100);
     } else {
-      this.$.copyToast.show();
+      this.toastMessage = message;
+      this.$.toast.show();
     }
   }
 
   private onHideToastClick() {
-    this.$.copyToast.hide();
+    this.$.toast.hide();
   }
 
 
diff --git a/chrome/browser/resources/lens/overlay/post_selection_renderer.ts b/chrome/browser/resources/lens/overlay/post_selection_renderer.ts
index 0b9713f..14a34aeb 100644
--- a/chrome/browser/resources/lens/overlay/post_selection_renderer.ts
+++ b/chrome/browser/resources/lens/overlay/post_selection_renderer.ts
@@ -187,7 +187,7 @@
     this.height = 0;
     this.width = 0;
     this.dispatchEvent(new CustomEvent(
-        'hide-detected-text-context-menu', {bubbles: true, composed: true}));
+        'hide-selected-region-context-menu', {bubbles: true, composed: true}));
     this.notifyPostSelectionUpdated();
   }
 
diff --git a/chrome/browser/resources/lens/overlay/selection_overlay.html b/chrome/browser/resources/lens/overlay/selection_overlay.html
index f8e3f8b..6c23306 100644
--- a/chrome/browser/resources/lens/overlay/selection_overlay.html
+++ b/chrome/browser/resources/lens/overlay/selection_overlay.html
@@ -53,6 +53,10 @@
     pointer-events: none;
   }
 
+  :host(:not([enable-copy-as-image])) .copy-as-image-context-menu-item,
+  :host(:not([enable-save-as-image])) .save-as-image-context-menu-item,
+  :host(:not([show-detected-text-context-menu-options]))
+      .detected-text-context-menu-option,
   :host(:not([show-translate-context-menu-item])) .translate-context-menu-item,
   :host([disable-shimmer]) #overlayShimmerCanvas,
   :host([disable-shimmer]) #overlayShimmer {
@@ -200,7 +204,7 @@
   }
 
   :host([show-selected-text-context-menu]) #selectedTextContextMenu,
-  :host([show-detected-text-context-menu]) #detectedTextContextMenu {
+  :host([show-selected-region-context-menu]) #selectedRegionContextMenu {
     animation: scale-in 250ms cubic-bezier(0.2, 0.0, 0, 1.0);
     opacity: 1;
     visibility: visible;
@@ -236,6 +240,10 @@
     mask-image: url('copy.svg');
   }
 
+  .menu-item-icon.download {
+    mask-image: url('download.svg');
+  }
+
   .menu-item-icon.translate {
     mask-image: url('translate.svg');
   }
@@ -356,21 +364,32 @@
         <span class="menu-item-label">$i18n{translate}</span>
       </div>
     </div>
-    <div id="detectedTextContextMenu" class="context-menu" role="menu"
-        style$="[[getContextMenuStyle(detectedTextContextMenuX,
-                                      detectedTextContextMenuY)]]"
+    <div id="selectedRegionContextMenu" class="context-menu" role="menu"
+        style$="[[getContextMenuStyle(selectedRegionContextMenuX,
+                                      selectedRegionContextMenuY)]]"
         on-pointerenter="handlePointerEnterContextMenu"
         on-pointerleave="handlePointerLeaveContextMenu">
-      <div class="context-menu-item" role="menuitem"
-          on-pointerup="handleSelectText">
+      <div class="context-menu-item detected-text-context-menu-option"
+          role="menuitem" on-pointerup="handleSelectText">
         <span class="menu-item-icon text"></span>
         <span class="menu-item-label">$i18n{selectText}</span>
       </div>
-      <div class="context-menu-item translate-context-menu-item" role="menuitem"
+      <div class="context-menu-item translate-context-menu-item
+          detected-text-context-menu-option" role="menuitem"
           on-pointerup="handleTranslateDetectedText">
         <span class="menu-item-icon translate"></span>
         <span class="menu-item-label">$i18n{translate}</span>
       </div>
+      <div class="context-menu-item copy-as-image-context-menu-item"
+          role="menuitem" on-pointerup="handleCopyAsImage">
+        <span class="menu-item-icon copy"></span>
+        <span class="menu-item-label">$i18n{copyAsImage}</span>
+      </div>
+      <div class="context-menu-item save-as-image-context-menu-item"
+          role="menuitem" on-pointerup="handleSaveAsImage">
+        <span class="menu-item-icon download"></span>
+        <span class="menu-item-label">$i18n{saveAsImage}</span>
+      </div>
     </div>
   </div>
 </div>
diff --git a/chrome/browser/resources/lens/overlay/selection_overlay.ts b/chrome/browser/resources/lens/overlay/selection_overlay.ts
index 93bb36a9..72e11923 100644
--- a/chrome/browser/resources/lens/overlay/selection_overlay.ts
+++ b/chrome/browser/resources/lens/overlay/selection_overlay.ts
@@ -22,6 +22,7 @@
 import type {BrowserProxy} from './browser_proxy.js';
 import {getFallbackTheme} from './color_utils.js';
 import {type CursorTooltipData, CursorTooltipType} from './cursor_tooltip.js';
+import type {CenterRotatedBox} from './geometry.mojom-webui.js';
 import {UserAction} from './lens.mojom-webui.js';
 import {INVOCATION_SOURCE} from './lens_overlay_app.js';
 import {recordLensOverlayInteraction} from './metrics_utils.js';
@@ -76,18 +77,12 @@
   selectionEndIndex: number;
 }
 
-export interface DetectedTextContextMenuData {
-  // The left-most position of the detected text.
-  left: number;
-  // The right-most position of the detected text.
-  right: number;
-  // The highest position of the detected text.
-  top: number;
-  // The lowest position of the detected text.
-  bottom: number;
-  // The selection start index of the text.
+export interface SelectedRegionContextMenuData {
+  // The bounds of the selected region.
+  box: CenterRotatedBox;
+  // The selection start index of the detected text, or -1 if none.
   selectionStartIndex: number;
-  // The end selection index of the text.
+  // The end selection index of the detected text, or -1 if none.
   selectionEndIndex: number;
 }
 
@@ -95,13 +90,13 @@
   $: {
     backgroundImageCanvas: HTMLCanvasElement,
     cursor: HTMLElement,
-    detectedTextContextMenu: HTMLElement,
     initialFlashScrim: HTMLDivElement,
     objectSelectionLayer: ObjectLayerElement,
     overlayShimmerCanvas: OverlayShimmerCanvasElement,
     overlayShimmer: OverlayShimmerElement,
     postSelectionRenderer: PostSelectionRendererElement,
     regionSelectionLayer: RegionSelectionElement,
+    selectedRegionContextMenu: HTMLElement,
     selectedTextContextMenu: HTMLElement,
     selectionOverlay: HTMLElement,
     textSelectionLayer: TextLayerElement,
@@ -149,15 +144,19 @@
         value: false,
         reflectToAttribute: true,
       },
-      showDetectedTextContextMenu: {
+      showSelectedRegionContextMenu: {
         type: Boolean,
         value: false,
         reflectToAttribute: true,
       },
+      showDetectedTextContextMenuOptions: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
       selectedTextContextMenuX: Number,
       selectedTextContextMenuY: Number,
-      detectedTextContextMenuX: Number,
-      detectedTextContextMenuY: Number,
+      selectedRegionContextMenuX: Number,
+      selectedRegionContextMenuY: Number,
       canvasHeight: Number,
       canvasWidth: Number,
       isPointerInside: Boolean,
@@ -172,6 +171,18 @@
         readOnly: true,
         value: loadTimeData.getBoolean('useShimmerCanvas'),
       },
+      enableCopyAsImage: {
+        type: Boolean,
+        readOnly: true,
+        value: loadTimeData.getBoolean('enableCopyAsImage'),
+        reflectToAttribute: true,
+      },
+      enableSaveAsImage: {
+        type: Boolean,
+        readOnly: true,
+        value: loadTimeData.getBoolean('enableSaveAsImage'),
+        reflectToAttribute: true,
+      },
       isClosing: {
         type: Boolean,
         reflectToAttribute: true,
@@ -207,12 +218,13 @@
   private isInitialSize: boolean = true;
   private showTranslateContextMenuItem: boolean = true;
   private showSelectedTextContextMenu: boolean;
-  private showDetectedTextContextMenu: boolean;
+  private showSelectedRegionContextMenu: boolean;
+  private showDetectedTextContextMenuOptions: boolean;
   // Location at which to show the context menus.
   private selectedTextContextMenuX: number;
   private selectedTextContextMenuY: number;
-  private detectedTextContextMenuX: number;
-  private detectedTextContextMenuY: number;
+  private selectedRegionContextMenuX: number;
+  private selectedRegionContextMenuY: number;
   // Width and height values for rendering the background image canvas as the
   // proper dimensions.
   private canvasHeight: number;
@@ -221,6 +233,9 @@
   // bounds of the screenshot and the part the user interacts with. This should
   // be used instead of call getBoundingClientRect().
   private selectionOverlayRect: DOMRect;
+  // The selected region on which the context menu is being displayed. Used as
+  // argument for copy and save as image calls.
+  private selectedRegionContextMenuBox: CenterRotatedBox;
   private highlightedText: string = '';
   private contentLanguage: string = '';
   private textSelectionStartIndex: number = -1;
@@ -234,6 +249,8 @@
   private currentGesture: GestureEvent = emptyGestureEvent();
   private disableShimmer: boolean;
   private useShimmerCanvas: boolean;
+  private enableCopyAsImage: boolean;
+  private enableSaveAsImage: boolean;
   // Whether the overlay is being shut down.
   private isClosing: boolean = false;
   // Whether the default background scrim is currently being darkened.
@@ -332,24 +349,33 @@
           this.textSelectionStartIndex = e.detail.selectionStartIndex;
           this.textSelectionEndIndex = e.detail.selectionEndIndex;
         });
-    this.eventTracker_.add(
-        document, 'show-detected-text-context-menu', (e: CustomEvent) => {
-          this.showDetectedTextContextMenu = true;
-          this.detectedTextContextMenuX = e.detail.left;
-          this.detectedTextContextMenuY = e.detail.bottom;
-          this.detectedTextStartIndex = e.detail.selectionStartIndex;
-          this.detectedTextEndIndex = e.detail.selectionEndIndex;
-        });
     this.eventTracker_.add(document, 'hide-selected-text-context-menu', () => {
       this.showSelectedTextContextMenu = false;
       this.textSelectionStartIndex = -1;
       this.textSelectionEndIndex = -1;
     });
-    this.eventTracker_.add(document, 'hide-detected-text-context-menu', () => {
-      this.showDetectedTextContextMenu = false;
-      this.detectedTextStartIndex = -1;
-      this.detectedTextEndIndex = -1;
-    });
+    this.eventTracker_.add(
+        document, 'show-selected-region-context-menu',
+        (e: CustomEvent<SelectedRegionContextMenuData>) => {
+          this.selectedRegionContextMenuX =
+              e.detail.box.box.x - e.detail.box.box.width / 2;
+          this.selectedRegionContextMenuY =
+              e.detail.box.box.y + e.detail.box.box.height / 2;
+          this.selectedRegionContextMenuBox = e.detail.box;
+          this.detectedTextStartIndex = e.detail.selectionStartIndex;
+          this.detectedTextEndIndex = e.detail.selectionEndIndex;
+          this.showDetectedTextContextMenuOptions =
+              this.detectedTextStartIndex !== -1 &&
+              this.detectedTextEndIndex !== -1;
+          this.showSelectedRegionContextMenu = this.enableCopyAsImage ||
+              this.enableSaveAsImage || this.showDetectedTextContextMenuOptions;
+        });
+    this.eventTracker_.add(
+        document, 'hide-selected-region-context-menu', () => {
+          this.showSelectedRegionContextMenu = false;
+          this.detectedTextStartIndex = -1;
+          this.detectedTextEndIndex = -1;
+        });
     this.eventTracker_.add(document, 'darken-extra-scrim-opacity', () => {
       this.darkenExtraScrim = true;
     });
@@ -826,7 +852,7 @@
         this.shadowRoot!.elementsFromPoint(event.clientX, event.clientY);
     // Do not intercept events that should go to the following elements.
     if (elementsAtPoint.includes(this.$.selectedTextContextMenu) ||
-        elementsAtPoint.includes(this.$.detectedTextContextMenu)) {
+        elementsAtPoint.includes(this.$.selectedRegionContextMenu)) {
       return true;
     }
     // Ignore multi touch events and non-left/right click events.
@@ -890,6 +916,22 @@
     recordLensOverlayInteraction(INVOCATION_SOURCE, UserAction.kTranslateText);
   }
 
+  private handleCopyAsImage() {
+    BrowserProxyImpl.getInstance().handler.copyImage(
+        this.selectedRegionContextMenuBox);
+    this.showSelectedRegionContextMenu = false;
+    this.dispatchEvent(new CustomEvent('copied-as-image', {
+      bubbles: true,
+      composed: true,
+    }));
+  }
+
+  private handleSaveAsImage() {
+    BrowserProxyImpl.getInstance().handler.saveAsImage(
+        this.selectedRegionContextMenuBox);
+    this.showSelectedRegionContextMenu = false;
+  }
+
   // Make the cursor disappear over the context menu, as if leaving the overlay.
   private handlePointerEnterContextMenu() {
     this.isPointerInside = false;
@@ -961,14 +1003,18 @@
         this.screenshotDataReceived.bind(this));
   }
 
-  getShowDetectedTextContextMenuForTesting() {
-    return this.showDetectedTextContextMenu;
+  getShowSelectedRegionContextMenuForTesting() {
+    return this.showSelectedRegionContextMenu;
   }
 
   getShowSelectedTextContextMenuForTesting() {
     return this.showSelectedTextContextMenu;
   }
 
+  getShowDetectedTextContextMenuOptionsForTesting() {
+    return this.showDetectedTextContextMenuOptions;
+  }
+
   handleSelectTextForTesting() {
     this.handleSelectText();
   }
@@ -984,6 +1030,14 @@
   handleTranslateForTesting() {
     this.handleTranslate();
   }
+
+  handleCopyAsImageForTesting() {
+    this.handleCopyAsImage();
+  }
+
+  handleSaveAsImageForTesting() {
+    this.handleSaveAsImage();
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/lens/overlay/side_panel/side_panel_browser_proxy.ts b/chrome/browser/resources/lens/overlay/side_panel/side_panel_browser_proxy.ts
index 1642ec7..fef41e44 100644
--- a/chrome/browser/resources/lens/overlay/side_panel/side_panel_browser_proxy.ts
+++ b/chrome/browser/resources/lens/overlay/side_panel/side_panel_browser_proxy.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import type {LensSidePanelPageHandlerInterface} from '../lens.mojom-webui.js';
-import {LensPageHandlerFactory, LensSidePanelPageCallbackRouter, LensSidePanelPageHandlerRemote} from '../lens.mojom-webui.js';
+import {LensSidePanelPageCallbackRouter, LensSidePanelPageHandlerFactory, LensSidePanelPageHandlerRemote} from '../lens.mojom-webui.js';
 
 let instance: SidePanelBrowserProxy|null = null;
 
@@ -19,7 +19,7 @@
       new LensSidePanelPageHandlerRemote();
 
   constructor() {
-    const factory = LensPageHandlerFactory.getRemote();
+    const factory = LensSidePanelPageHandlerFactory.getRemote();
     factory.createSidePanelPageHandler(
         this.handler.$.bindNewPipeAndPassReceiver(),
         this.callbackRouter.$.bindNewPipeAndPassRemote());
diff --git a/chrome/browser/resources/lens/overlay/text_layer.ts b/chrome/browser/resources/lens/overlay/text_layer.ts
index a5f3d39..405e9e9 100644
--- a/chrome/browser/resources/lens/overlay/text_layer.ts
+++ b/chrome/browser/resources/lens/overlay/text_layer.ts
@@ -23,7 +23,7 @@
 import {UserAction} from './lens.mojom-webui.js';
 import {INVOCATION_SOURCE} from './lens_overlay_app.js';
 import {recordLensOverlayInteraction} from './metrics_utils.js';
-import type {CursorData, DetectedTextContextMenuData, SelectedTextContextMenuData} from './selection_overlay.js';
+import type {CursorData, SelectedRegionContextMenuData, SelectedTextContextMenuData} from './selection_overlay.js';
 import {CursorType} from './selection_utils.js';
 import type {GestureEvent} from './selection_utils.js';
 import type {BackgroundImageData, Line, Paragraph, Text, TranslatedLine, TranslatedParagraph, Word} from './text.mojom-webui.js';
@@ -226,6 +226,12 @@
   // IoU threshold for finding words in region.
   private selectTextTriggerThreshold: number =
       loadTimeData.getValue('selectTextTriggerThreshold');
+  // Timeout for onTextReceived. We do not want to show the selected region
+  // context menu until either the text is received or the timeout elapses.
+  private textReceivedTimeout: number =
+      loadTimeData.getValue('textReceivedTimeout');
+  private textReceivedTimeoutID: number = 0;
+  private textReceivedTimeoutElapsedOrCleared = false;
   private browserProxy: BrowserProxy = BrowserProxyImpl.getInstance();
 
   override ready() {
@@ -260,6 +266,10 @@
       this.browserProxy.callbackRouter.setTextSelection.addListener(
           this.selectWords.bind(this)),
     ];
+
+    this.textReceivedTimeoutID = setTimeout(() => {
+      this.textReceivedTimeoutElapsedOrCleared = true;
+    }, this.textReceivedTimeout);
   }
 
   override disconnectedCallback() {
@@ -300,26 +310,36 @@
   }
 
   private detectTextInRegion(box: CenterRotatedBox) {
-    const selection =
-        findWordsInRegion(this.renderedWords, box, this.selectionOverlayRect);
-    if (selection.iou < this.selectTextTriggerThreshold) {
+    // If we are still waiting for the text, hide the context menu.
+    if (!this.textReceivedTimeoutElapsedOrCleared) {
       this.dispatchEvent(new CustomEvent(
-          'hide-detected-text-context-menu', {bubbles: true, composed: true}));
+          'hide-selected-region-context-menu',
+          {bubbles: true, composed: true}));
       return;
     }
-    const left = box.box.x - box.box.width / 2;
-    const right = box.box.x + box.box.width / 2;
-    const top = box.box.y - box.box.height / 2;
-    const bottom = box.box.y + box.box.height / 2;
-    this.dispatchEvent(new CustomEvent<DetectedTextContextMenuData>(
-        'show-detected-text-context-menu', {
+
+    const selection =
+        findWordsInRegion(this.renderedWords, box, this.selectionOverlayRect);
+    // Words may be found in the region even if the IOU threshold is not met.
+    // If IOU threshold is not met, behave as if no words were found. Show the
+    // context menu but do not send the selection indices so that options for
+    // detected text are not shown.
+    if (selection.iou < this.selectTextTriggerThreshold) {
+      this.dispatchEvent(new CustomEvent<SelectedRegionContextMenuData>(
+          'show-selected-region-context-menu', {
+            bubbles: true,
+            composed: true,
+            detail: {box, selectionStartIndex: -1, selectionEndIndex: -1},
+          }));
+      return;
+    }
+
+    this.dispatchEvent(new CustomEvent<SelectedRegionContextMenuData>(
+        'show-selected-region-context-menu', {
           bubbles: true,
           composed: true,
           detail: {
-            left,
-            right,
-            top,
-            bottom,
+            box,
             selectionStartIndex: selection.startIndex,
             selectionEndIndex: selection.endIndex,
           },
@@ -535,7 +555,7 @@
     this.dispatchEvent(new CustomEvent(
         'hide-selected-text-context-menu', {bubbles: true, composed: true}));
     this.dispatchEvent(new CustomEvent(
-        'hide-detected-text-context-menu', {bubbles: true, composed: true}));
+        'hide-selected-region-context-menu', {bubbles: true, composed: true}));
     this.dispatchTextHighlightState();
   }
 
@@ -664,6 +684,9 @@
       this.computeTranslatedWordBoundingBoxes();
     });
 
+    this.textReceivedTimeoutElapsedOrCleared = true;
+    clearTimeout(this.textReceivedTimeoutID);
+
     // Used to notify the post selection renderer so that, if a region has
     // already been selected, text in the region can be detected.
     this.dispatchEvent(new CustomEvent(
diff --git a/chrome/browser/resources/lens/region_search.html b/chrome/browser/resources/lens/region_search.html
index 435bc2b3..3fa41ba 100644
--- a/chrome/browser/resources/lens/region_search.html
+++ b/chrome/browser/resources/lens/region_search.html
@@ -6,7 +6,7 @@
 </head>
 <body>
   <iframe id="content"
-      src="chrome-untrusted://lens"
+      src="chrome-untrusted://lens-overlay"
       allow="cross-origin-isolated"></iframe>
   <script type="module" src="app.js"></script>
 </body>
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index 1f94c25..19835cc 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -1684,12 +1684,26 @@
   // Highlights or rehighlights the current granularity, sentence or word.
   highlightCurrentGranularity(
       axNodeIds: number[], scrollIntoView: boolean = true) {
-    if (this.wordBoundaryState.mode ===
-            WordBoundaryMode.BOUNDARIES_NOT_SUPPORTED ||
-        !this.shouldUseWordHighlighting()) {
-      this.highlightCurrentSentence(axNodeIds, scrollIntoView);
-    } else {
-      this.highlightCurrentWord();
+    const highlightGranularity = this.getEffectiveHighlightingGranularity_();
+    switch (highlightGranularity) {
+      case chrome.readingMode.noHighlighting:
+      // Even without highlighting, we still calculate the sentence highlight,
+      // so that it's visible as soon as the user turns on sentence
+      // highlighting. The highlight will not be visible, since the highlight
+      // color in this case will be transparent.
+      case chrome.readingMode.sentenceHighlighting:
+        this.highlightCurrentSentence(axNodeIds, scrollIntoView);
+        break;
+      case chrome.readingMode.wordHighlighting:
+        this.highlightCurrentWord();
+        break;
+      case chrome.readingMode.phraseHighlighting:
+        this.highlightCurrentPhrase();
+        break;
+      case chrome.readingMode.autoHighlighting:
+      default:
+        // This cannot happen, but ensures the switch statement is exhaustive.
+        assert(false, 'invalid value for effective highlight');
     }
   }
 
@@ -1813,10 +1827,25 @@
       if (event.name === 'word') {
         this.updateBoundary(event.charIndex);
 
-        // Only update the highlighting with word highlights if they should be
-        // used.
-        if (this.shouldUseWordHighlighting()) {
-          this.highlightCurrentWord();
+        const highlightGranularity =
+            this.getEffectiveHighlightingGranularity_();
+        switch (highlightGranularity) {
+          case chrome.readingMode.noHighlighting:
+          case chrome.readingMode.sentenceHighlighting:
+            // No need to update the highlight on word boundary events if
+            // highlighting is off or if sentence highlighting is used.
+            break;
+          case chrome.readingMode.wordHighlighting:
+            this.highlightCurrentWord();
+            break;
+          case chrome.readingMode.phraseHighlighting:
+            this.highlightCurrentPhrase();
+            break;
+          case chrome.readingMode.autoHighlighting:
+          default:
+            // This cannot happen, but ensures the switch statement is
+            // exhaustive.
+            assert(false, 'invalid value for effective highlight');
         }
       }
     });
@@ -1931,8 +1960,16 @@
     return utteranceText;
   }
 
-  // TODO(b/301131238): Verify all edge cases.
   highlightCurrentWord() {
+    this.highlightCurrentWordOrPhrase_(false);
+  }
+
+  highlightCurrentPhrase() {
+    this.highlightCurrentWordOrPhrase_(true);
+  }
+
+  // TODO(b/301131238): Verify all edge cases.
+  private highlightCurrentWordOrPhrase_(highlightPhrases: boolean) {
     // Word highlights can be called quite frequently which can create some
     // misordering, so just make sure we've cleared the previous word highlight
     // before showing the next one.
@@ -1940,7 +1977,8 @@
     const index = this.wordBoundaryState.speechUtteranceStartIndex +
         this.wordBoundaryState.previouslySpokenIndex;
     const highlightNodes =
-        chrome.readingMode.getHighlightForCurrentSegmentIndex(index);
+        chrome.readingMode.getHighlightForCurrentSegmentIndex(
+            index, highlightPhrases);
     let anyHighlighted: boolean = false;
     for (let i = 0; i < highlightNodes.length; i++) {
       const highlightNode = highlightNodes[i].nodeId;
@@ -2106,12 +2144,54 @@
     this.resetToDefaultWordBoundaryState();
   }
 
-  private shouldUseWordHighlighting(): boolean {
-    // Word highlighting should only be used for speech rates less than or
-    // equal to 1x speed. It should be skipped on espeak voices, since espeak
-    // boundaries are different than Google TTS word boundaries.
-    return chrome.readingMode.isAutomaticWordHighlightingEnabled &&
-        getCurrentSpeechRate() <= 1 && !isEspeak(this.selectedVoice_);
+  private getEffectiveHighlightingGranularity_(): number {
+    // Parse all of the conditions that control highlighting and return the
+    // effective highlighting granularity.
+    const highlight = chrome.readingMode.highlightGranularity;
+
+    if (highlight === chrome.readingMode.noHighlighting ||
+        highlight === chrome.readingMode.sentenceHighlighting) {
+      return highlight;
+    }
+
+    if (!chrome.readingMode.isAutomaticWordHighlightingEnabled ||
+        this.wordBoundaryState.mode ===
+            WordBoundaryMode.BOUNDARIES_NOT_SUPPORTED ||
+        isEspeak(this.selectedVoice_)) {
+      // Fall back where word highlighting is not possible. Since espeak
+      // boundaries are different than Google TTS word boundaries, fall back to
+      // sentence boundaries in that case too.
+      return chrome.readingMode.sentenceHighlighting;
+    }
+
+    const currentSpeechRate: number = getCurrentSpeechRate();
+
+    if (!chrome.readingMode.isPhraseHighlightingEnabled) {
+      // Choose sentence highlighting for fast voices.
+      if (currentSpeechRate > 1.2 &&
+          highlight === chrome.readingMode.autoHighlighting) {
+        return chrome.readingMode.sentenceHighlighting;
+      }
+
+      // In other cases where phrase highilghting is off, choose word
+      // highlighting.
+      return chrome.readingMode.wordHighlighting;
+    }
+
+    // TODO(crbug.com/364327601): Check that the language of the page should
+    // be English for phrase highlighting.
+    if (highlight === chrome.readingMode.autoHighlighting) {
+      if (currentSpeechRate <= 0.8) {
+        return chrome.readingMode.wordHighlighting;
+      } else if (currentSpeechRate >= 2.0) {
+        return chrome.readingMode.sentenceHighlighting;
+      } else {
+        return chrome.readingMode.phraseHighlighting;
+      }
+    }
+
+    // In other cases, return what the user selected (i.e. word/phrase).
+    return highlight;
   }
 
   protected onSelectVoice_(
@@ -2362,6 +2442,11 @@
     chrome.readingMode.onHighlightGranularityChanged(changedHighlight);
     // Apply highlighting changes to the DOM.
     this.styleUpdater_.setHighlight();
+
+    // TODO(crbug.com/366002886): Re-highlight with the new granularity. In
+    // particular, when switching from word or phrase to sentence, the sentence
+    // highlight needs to be recalculated.
+
     // TODO(crbug.com/364546547): Log these highlight granularity changes when
     // the phrase menu is shown. (Toggles are already logged in the toolbar.)
   }
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
index 16ce19a..9eb617a6 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
@@ -383,14 +383,17 @@
     // Returns a list of node ids and ranges (start and length) associated with
     // the index within the given text segment. The intended use is for
     // highlighting the ranges. Note that a highlight can span over multiple
-    // nodes in certain cases.
+    // nodes in certain cases. If the `phrases` argument is `true`, the text
+    // ranges for the containing phrase are returned, otherwise the text ranges
+    // for the word are returned.
     //
     // For example, for a segment of text composed of two nodes:
     // Node 1: "Hello, this is a "
     // Node 2: "segment of text."
     // An index of "20" will return the node id associated with node 2, a start
     // index of 0, and a length of 8 (covering the word "segment ").
-    function getHighlightForCurrentSegmentIndex(index: number):
+    function getHighlightForCurrentSegmentIndex(
+        index: number, phrases: boolean):
         Array<{nodeId: number, start: number, length: number}>;
   }
 }
diff --git a/chrome/browser/resources/signin/BUILD.gn b/chrome/browser/resources/signin/BUILD.gn
index 53b1d11..1f8948b 100644
--- a/chrome/browser/resources/signin/BUILD.gn
+++ b/chrome/browser/resources/signin/BUILD.gn
@@ -70,6 +70,8 @@
       "managed_user_profile_notice/managed_user_profile_notice_browser_proxy.ts",
       "managed_user_profile_notice/managed_user_profile_notice_disclosure.html.ts",
       "managed_user_profile_notice/managed_user_profile_notice_disclosure.ts",
+      "managed_user_profile_notice/managed_user_profile_notice_value_prop.html.ts",
+      "managed_user_profile_notice/managed_user_profile_notice_value_prop.ts",
       "managed_user_profile_notice/managed_user_profile_notice_state.html.ts",
       "managed_user_profile_notice/managed_user_profile_notice_state.ts",
       "profile_customization/profile_customization_app.html.ts",
@@ -112,6 +114,7 @@
       "managed_user_profile_notice/managed_user_profile_notice_app.css",
       "managed_user_profile_notice/managed_user_profile_notice_disclosure.css",
       "managed_user_profile_notice/managed_user_profile_notice_state.css",
+      "managed_user_profile_notice/managed_user_profile_notice_value_prop.css",
       "profile_customization/profile_customization_app.css",
       "signin_email_confirmation/signin_email_confirmation_app.css",
       "signin_error/signin_error_app.css",
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.html.ts b/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.html.ts
index 44cabc3..11da4fe9 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.html.ts
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.html.ts
@@ -53,8 +53,7 @@
         ?disabled="${this.disableProceedButton_}">
       ${this.proceedLabel_}
     </cr-button>
-    <cr-button id="cancel-button" @click="${this.onCancel_}"
-        ?hidden="${!this.showCancelButton_}">
+    <cr-button id="cancel-button" @click="${this.onCancel_}">
       $i18n{cancelLabel}
     </cr-button>
   </div>
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.ts b/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.ts
index c1f95a0..36d71079 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.ts
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/legacy_managed_user_profile_notice_app.ts
@@ -87,9 +87,6 @@
       /** The label for the button to proceed with the flow */
       proceedLabel_: {type: String},
 
-      /** Whether to show the cancel button on the screen */
-      showCancelButton_: {type: Boolean},
-
       disableProceedButton_: {type: Boolean},
 
       linkData_: {
@@ -111,7 +108,6 @@
   protected proceedLabel_: string;
   protected disableProceedButton_: boolean = false;
   protected linkData_: boolean = false;
-  protected showCancelButton_: boolean = true;
   private defaultProceedLabel_: string;
   private managedUserProfileNoticeBrowserProxy_:
       ManagedUserProfileNoticeBrowserProxy =
@@ -158,7 +154,6 @@
     this.enterpriseInfo_ = info.enterpriseInfo;
     this.defaultProceedLabel_ = info.proceedLabel;
     this.proceedLabel_ = this.defaultProceedLabel_;
-    this.showCancelButton_ = info.showCancelButton;
     this.linkData_ = info.checkLinkDataCheckboxByDefault;
   }
 
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.html.ts b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.html.ts
index 7a2b53c..5eef4f2c 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.html.ts
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.html.ts
@@ -12,6 +12,13 @@
     <img class="tangible-sync-style-left-banner" alt="">
     <img class="tangible-sync-style-right-banner" alt="">
     <div id="content-container">
+      ${this.showValueProposition_ ? html`
+        <managed-user-profile-notice-value-prop id="value-prop"
+            title="$i18n{signinIntoChrome}" subtitle="$i18n{valuePropSubtitle}"
+            picture-url="${this.pictureUrl_}"
+            email="${this.email_}" account-name="${this.accountName_}">
+        ` : ''}
+        </managed-user-profile-notice-value-prop>
       ${this.showDisclosure_ ? html`
         <managed-user-profile-notice-disclosure id="disclosure"
             title="${this.title_}" subtitle="${this.subtitle_}"
@@ -55,7 +62,7 @@
     </cr-button>
     <cr-button id="cancel-button" @click="${this.onCancel_}"
         ?hidden="${!this.allowCancel_()}">
-      $i18n{cancelLabel}
+      ${this.cancelLabel_}
     </cr-button>
   </div>
 ` : ''}
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.ts b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.ts
index bb1782c..ef13bef 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.ts
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_app.ts
@@ -6,6 +6,7 @@
 import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import './strings.m.js';
 import './managed_user_profile_notice_disclosure.js';
+import './managed_user_profile_notice_value_prop.js';
 import './managed_user_profile_notice_state.js';
 import '//resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
 
@@ -79,7 +80,9 @@
       },
 
       /** The label for the button to proceed with the flow */
+      continueAs_: {type: String},
       proceedLabel_: {type: String},
+      cancelLabel_: {type: String},
 
       /** Whether to show the cancel button on the screen */
       showCancelButton_: {type: Boolean},
@@ -99,6 +102,9 @@
     };
   }
 
+  protected email_: string;
+  protected accountName_: string;
+  private continueAs_: string;
   protected showEnterpriseBadge_: boolean = false;
   protected pictureUrl_: string;
   protected title_: string;
@@ -106,10 +112,12 @@
   private enterpriseInfo_: string;
   protected isModalDialog_: boolean = loadTimeData.getBoolean('isModalDialog');
   protected proceedLabel_: string;
+  protected cancelLabel_: string;
   protected disableProceedButton_: boolean = false;
   private showCancelButton_: boolean = true;
   private currentState_: State = State.DISCLOSURE;
-  protected showDisclosure_: boolean = true;
+  protected showValueProposition_: boolean = false;
+  protected showDisclosure_: boolean = false;
   protected showProcessing_: boolean = false;
   protected showSuccess_: boolean = false;
   protected showTimeout_: boolean = false;
@@ -126,6 +134,11 @@
         changedProperties as Map<PropertyKey, unknown>;
 
     if (changedPrivateProperties.has('currentState_')) {
+      this.cancelLabel_ = this.computeCancelLabel_();
+    }
+
+    if (changedPrivateProperties.has('currentState_') ||
+        changedPrivateProperties.has('continueAs_')) {
       this.proceedLabel_ = this.computeProceedLabel_();
     }
   }
@@ -138,8 +151,10 @@
     this.addWebUiListener(
         'on-profile-info-changed',
         (info: ManagedUserProfileInfo) => this.setProfileInfo_(info));
-    this.managedUserProfileNoticeBrowserProxy_.initialized().then(
-        info => this.setProfileInfo_(info));
+    this.managedUserProfileNoticeBrowserProxy_.initialized().then(info => {
+      this.setProfileInfo_(info);
+      this.updateCurrentState_(loadTimeData.getInteger('initialState'));
+    });
   }
 
   /** Called when the proceed button is clicked. */
@@ -156,15 +171,18 @@
 
   private setProfileInfo_(info: ManagedUserProfileInfo) {
     this.pictureUrl_ = info.pictureUrl;
+    this.email_ = info.email;
+    this.accountName_ = info.accountName;
+    this.continueAs_ = info.continueAs;
     this.showEnterpriseBadge_ = info.showEnterpriseBadge;
     this.title_ = info.title;
     this.subtitle_ = info.subtitle;
     this.enterpriseInfo_ = info.enterpriseInfo;
-    this.showCancelButton_ = info.showCancelButton;
   }
 
   private updateCurrentState_(state: State) {
     this.currentState_ = state;
+    this.showValueProposition_ = state === State.VALUE_PROPOSITION;
     this.showDisclosure_ = state === State.DISCLOSURE;
     this.showProcessing_ = state === State.PROCESSING;
     this.showSuccess_ = state === State.SUCCESS;
@@ -179,11 +197,20 @@
   }
 
   protected allowCancel_() {
-    return this.showCancelButton_ && this.showDisclosure_;
+    return this.showDisclosure_ || this.showValueProposition_;
+  }
+
+  private computeCancelLabel_() {
+    return this.currentState_ === State.VALUE_PROPOSITION &&
+            !loadTimeData.getBoolean('enforcedByPolicy') ?
+        this.i18n('cancelValueProp') :
+        this.i18n('cancelLabel');
   }
 
   private computeProceedLabel_() {
     switch (this.currentState_) {
+      case State.VALUE_PROPOSITION:
+        return this.continueAs_;
       case State.DISCLOSURE:
       case State.PROCESSING:
         return this.i18n('continueLabel');
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_browser_proxy.ts b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_browser_proxy.ts
index 560abf9..917ba30 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_browser_proxy.ts
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_browser_proxy.ts
@@ -15,17 +15,20 @@
   SUCCESS = 2,
   TIMEOUT = 3,
   ERROR = 4,
+  VALUE_PROPOSITION = 5,
 }
 
 // Managed user profile info sent from C++.
 export interface ManagedUserProfileInfo {
+  accountName: string;
+  continueAs: string;
+  email: string;
   pictureUrl: string;
   showEnterpriseBadge: boolean;
   title: string;
   subtitle: string;
   enterpriseInfo: string;
   proceedLabel: string;
-  showCancelButton: boolean;
   checkLinkDataCheckboxByDefault: boolean;
 }
 
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_disclosure.css b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_disclosure.css
index 32bd565..4c233ecc 100644
--- a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_disclosure.css
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_disclosure.css
@@ -11,7 +11,7 @@
  * #include=signin-shared tangible-sync-style-shared-lit
  * #css_wrapper_metadata_end */
 
-:host() {
+:host {
   color: var(--cr-primary-text-color);
 }
 
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.css b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.css
new file mode 100644
index 0000000..94358f3
--- /dev/null
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.css
@@ -0,0 +1,90 @@
+/* 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=//resources/cr_elements/cr_shared_vars.css.js
+ * #import=/tangible_sync_style_shared_lit.css.js
+ * #scheme=relative
+ * #include=tangible-sync-style-shared-lit
+ * #css_wrapper_metadata_end */
+
+:host {
+  color: var(--cr-primary-text-color);
+  width: 64vw;
+}
+
+main {
+  align-items: center;
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  justify-content: center;
+  padding-inline: var(--tangible-sync-style-banner-width);
+  margin-inline: auto;
+  width: 368px;
+}
+
+.title {
+  margin: 0;
+}
+
+.subtitle {
+  text-align: center;
+}
+
+#product-logo {
+  height: 34px;
+  margin-block-end: 16px;
+  width: 34px;
+}
+
+.pill {
+  align-items: center;
+  background-color: var(--md-background-color);
+  border-radius: 16px;
+  box-sizing: border-box;
+  display: flex;
+  gap: 12px;
+  height: 64px;
+  padding: 12px;
+}
+
+.pill .avatar {
+  flex: 0 0 40px;
+  border-radius: 50%;
+  height: 40px;
+  width: 40px;
+}
+
+.pill .text-container {
+  flex: 1 1 auto;
+  padding-inline-end: 12px;
+}
+
+.pill .icon {
+  flex: 0 0 20px;
+  height: 20px;
+  width: 20px;
+}
+
+.account-name {
+  font-size: 0.875rem;
+  font-weight: 500;
+  line-height: 20px;
+  margin: 0;
+  text-overflow: ellipsis;
+}
+
+.email {
+  font-size: 0.75rem;
+  margin: 0;
+  text-overflow: ellipsis;
+}
+
+@media (prefers-color-scheme: dark) {
+  .pill {
+    background-color: var(--cr-fallback-color-surface);
+  }
+}
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.html.ts b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.html.ts
new file mode 100644
index 0000000..3f18c90
--- /dev/null
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.html.ts
@@ -0,0 +1,30 @@
+// 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 {ManagedUserProfileNoticeValuePropElement} from './managed_user_profile_notice_value_prop.js';
+
+export function getHtml(this: ManagedUserProfileNoticeValuePropElement) {
+  return html`<!--_html_template_start_-->
+<main class="tangible-sync-style">
+  <img id="product-logo" alt="Chrome logo" role="presentation"
+      src="chrome://theme/current-channel-logo@2x">
+  <h1 class="title">
+    ${this.title}
+  </h1>
+  <p class="subtitle">
+    ${this.subtitle}
+  </p>
+  <div class="pill">
+      <img id="avatar" class="avatar" alt="" src="${this.pictureUrl}">
+      <div class="text-container">
+        <p class="account-name">${this.accountName}</p>
+        <p class="email">${this.email}</p>
+      </div>
+      <cr-icon class="icon" icon="cr:domain"></cr-icon>
+  </div>
+</main>
+<!--_html_template_end_-->`;
+}
diff --git a/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.ts b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.ts
new file mode 100644
index 0000000..cbc89e73
--- /dev/null
+++ b/chrome/browser/resources/signin/managed_user_profile_notice/managed_user_profile_notice_value_prop.ts
@@ -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.
+
+import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/cr_elements/cr_icon/cr_icon.js';
+import 'chrome://resources/cr_elements/icons.html.js';
+
+import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+
+import {getCss} from './managed_user_profile_notice_value_prop.css.js';
+import {getHtml} from './managed_user_profile_notice_value_prop.html.js';
+
+export class ManagedUserProfileNoticeValuePropElement extends CrLitElement {
+  static get is() {
+    return 'managed-user-profile-notice-value-prop';
+  }
+
+  static override get styles() {
+    return getCss();
+  }
+
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
+    return {
+      pictureUrl: {type: String},
+      title: {type: String},
+      subtitle: {type: String},
+      email: {type: String},
+      accountName: {type: String},
+    };
+  }
+
+  pictureUrl: string;
+  override title: string;
+  subtitle: string;
+  email: string;
+  accountName: string;
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'managed-user-profile-notice-value-prop':
+        ManagedUserProfileNoticeValuePropElement;
+  }
+}
+
+customElements.define(
+    ManagedUserProfileNoticeValuePropElement.is,
+    ManagedUserProfileNoticeValuePropElement);
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html.ts b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html.ts
index fd4c6fd..9449d6d 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html.ts
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html.ts
@@ -51,8 +51,9 @@
 </div>
 
 <cr-dialog id="forceSigninErrorDialog">
-  <div slot="title" class="key-text">${this.forceSigninErrorDialogTitle_}</div>
-  <div slot="body" class="warning-message">
+  <div slot="title" id="dialog-title" class="key-text">
+    ${this.forceSigninErrorDialogTitle_}</div>
+  <div slot="body" id="dialog-body" class="warning-message">
     ${this.forceSigninErrorDialogBody_}
   </div>
   <div slot="button-container" class="button-container">
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_failure.html.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_failure.html.ts
index c0bd0a8..a1395c9 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_failure.html.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_failure.html.ts
@@ -11,12 +11,6 @@
   return html`<!--_html_template_start_-->
 <div class="auto-tab-groups-container">
   <div class="auto-tab-groups-text-container">
-    <div id="header"
-        class="auto-tab-groups-header"
-        aria-live="polite"
-        aria-relevant="all">
-      ${this.getTitle(this.error)}
-    </div>
     <div class="auto-tab-groups-body">
       <localized-link localized-string="${this.getBody_()}"
           @link-clicked="${this.onCheckNow_}"></localized-link>
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_failure.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_failure.ts
index cdbc95e..ae3f2fe2 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_failure.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_failure.ts
@@ -42,17 +42,6 @@
     return getHtml.bind(this)();
   }
 
-  getTitle(error: TabOrganizationError): string {
-    switch (error) {
-      case TabOrganizationError.kGrouping:
-        return loadTimeData.getString('failureTitleGrouping');
-      case TabOrganizationError.kGeneric:
-        return loadTimeData.getString('failureTitleGeneric');
-      default:
-        return '';
-    }
-  }
-
   protected getBody_(): string {
     switch (this.error) {
       case TabOrganizationError.kGrouping:
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.html.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.html.ts
index e648312..bfaf3f1 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.html.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.html.ts
@@ -65,6 +65,7 @@
           @close="${this.onTabRemove_}"
           @focus="${this.onTabFocus_}"
           @blur="${this.onTabBlur_}"
+          close-button-icon="tab-search:remove"
           in-suggested-group>
       </tab-search-item>
     `)}
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_in_progress.html.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_in_progress.html.ts
index 7f3cfa30..6b5403c4 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_in_progress.html.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_in_progress.html.ts
@@ -9,31 +9,23 @@
 export function getHtml(this: AutoTabGroupsInProgressElement) {
   // clang-format off
   return html`<!--_html_template_start_-->
-<div class="auto-tab-groups-container">
-  <div id="header"
-      class="auto-tab-groups-header"
-      aria-live="polite"
-      aria-relevant="all">
-    ${this.getTitle()}
-  </div>
-  <div id="loading-container">
-    <cr-loading-gradient>
-        <svg width="100%" height="191">
-          <clipPath>
-            <rect x="0" y="0" width="100%" height="35" rx="8"></rect>
-            <rect x="0" y="55" width="40" height="40" rx="8"></rect>
-            <rect x="56" y="57" width="116" height="16" rx="4"></rect>
-            <rect x="56" y="105" width="116" height="16" rx="4"></rect>
-            <rect x="56" y="153" width="116" height="16" rx="4"></rect>
-            <rect x="56" y="79" width="76" height="14" rx="4"></rect>
-            <rect x="56" y="127" width="76" height="14" rx="4"></rect>
-            <rect x="56" y="175" width="76" height="14" rx="4"></rect>
-            <rect x="0" y="103" width="40" height="40" rx="8"></rect>
-            <rect x="0" y="151" width="40" height="40" rx="8"></rect>
-          </clipPath>
-        </svg>
-      </cr-loading-gradient>
-    </div>
+<div id="loading-container">
+  <cr-loading-gradient>
+      <svg width="100%" height="191">
+        <clipPath>
+          <rect x="0" y="0" width="100%" height="35" rx="8"></rect>
+          <rect x="0" y="55" width="40" height="40" rx="8"></rect>
+          <rect x="56" y="57" width="116" height="16" rx="4"></rect>
+          <rect x="56" y="105" width="116" height="16" rx="4"></rect>
+          <rect x="56" y="153" width="116" height="16" rx="4"></rect>
+          <rect x="56" y="79" width="76" height="14" rx="4"></rect>
+          <rect x="56" y="127" width="76" height="14" rx="4"></rect>
+          <rect x="56" y="175" width="76" height="14" rx="4"></rect>
+          <rect x="0" y="103" width="40" height="40" rx="8"></rect>
+          <rect x="0" y="151" width="40" height="40" rx="8"></rect>
+        </clipPath>
+      </svg>
+    </cr-loading-gradient>
   </div>
 </div>
 <!--_html_template_end_-->`;
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_in_progress.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_in_progress.ts
index 819eee6c..42cf6ea6 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_in_progress.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_in_progress.ts
@@ -3,9 +3,7 @@
 // found in the LICENSE file.
 
 import 'chrome://resources/cr_elements/cr_loading_gradient/cr_loading_gradient.js';
-import '../strings.m.js';
 
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 import {getCss} from './auto_tab_groups_in_progress.css.js';
@@ -30,10 +28,6 @@
   override render() {
     return getHtml.bind(this)();
   }
-
-  getTitle(): string {
-    return loadTimeData.getString('inProgressTitle');
-  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_not_started.html.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_not_started.html.ts
index ed1ab3b..12585ec 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_not_started.html.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_not_started.html.ts
@@ -14,16 +14,10 @@
 <div class="auto-tab-groups-container">
   <auto-tab-groups-not-started-image></auto-tab-groups-not-started-image>
   <div class="auto-tab-groups-text-container">
-    <div id="header"
-        class="auto-tab-groups-header"
-        aria-live="polite"
-        aria-relevant="all">
-      ${this.getTitle()}
-    </div>
     <div class="auto-tab-groups-body">
       ${this.getBody_()}
       ${this.showFre ? html`
-<table class="bullet-list">
+        <table class="bullet-list">
           <tr>
             <td>
               <cr-icon icon="tab-search:plant" aria-hidden="true"></cr-icon>
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_not_started.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_not_started.ts
index 0ff91d1c..28805f7 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_not_started.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_not_started.ts
@@ -68,11 +68,6 @@
     this.addWebUiListener('account-info-changed', this.setSignedIn_.bind(this));
   }
 
-  getTitle(): string {
-    return loadTimeData.getString(
-        this.showFre ? 'notStartedTitleFRE' : 'notStartedTitle');
-  }
-
   private setSignedIn_(signedIn: boolean) {
     this.signedIn_ = signedIn;
   }
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.css b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.css
index 1703ba6..5c3c745 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.css
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.css
@@ -47,6 +47,10 @@
              slideOut 250ms var(--standard-curve) forwards;
 }
 
+.auto-tab-groups-header {
+  padding-bottom: 16px;
+}
+
 #body {
   margin: var(--mwb-list-item-horizontal-margin);
 }
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.html.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.html.ts
index 0cf8b86..b47dbad 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.html.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.html.ts
@@ -11,6 +11,12 @@
   return html`<!--_html_template_start_-->
 <div id="contents">
   <div id="body">
+    <div id="header"
+        class="auto-tab-groups-header"
+        aria-live="polite"
+        aria-relevant="all">
+      ${this.getTitle_()}
+    </div>
     <auto-tab-groups-not-started id="notStarted"
         ?shown="${this.isState_(TabOrganizationState.kNotStarted)}"
         model-strategy="${this.modelStrategy_}"
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.ts
index 854bdad7..1d78a28 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.ts
@@ -9,7 +9,6 @@
 import './auto_tab_groups_not_started.js';
 import './auto_tab_groups_results.js';
 
-import {getInstance as getAnnouncerInstance} from '//resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
 import {CrFeedbackOption} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
 import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -194,33 +193,17 @@
     if (!changedState) {
       return;
     }
-    const announcer = getAnnouncerInstance();
-    switch (state) {
-      case TabOrganizationState.kInitializing:
-        break;
-      case TabOrganizationState.kNotStarted:
-        announcer.announce(this.$.notStarted.getTitle());
-        break;
-      case TabOrganizationState.kInProgress:
-        announcer.announce(this.$.inProgress.getTitle());
-        // Ensure the loading state appears for a sufficient amount of time, so
-        // as to not appear jumpy if the request completes quickly.
-        this.futureState_ = TabOrganizationState.kInProgress;
-        setTimeout(() => this.applyFutureState_(), MIN_LOADING_ANIMATION_MS);
-        break;
-      case TabOrganizationState.kSuccess:
-        announcer.announce(this.$.results.getTitle());
-        // Wait until the new state is visible after the transition to focus on
-        // the new UI.
-        this.$.results.addEventListener('animationend', () => {
-          this.$.results.focusInput();
-        }, {once: true});
-        break;
-      case TabOrganizationState.kFailure:
-        announcer.announce(this.$.failure.getTitle(this.getSessionError_()));
-        break;
-      default:
-        assertNotReached('Invalid tab organization state');
+    if (state === TabOrganizationState.kInProgress) {
+      // Ensure the loading state appears for a sufficient amount of time, so
+      // as to not appear jumpy if the request completes quickly.
+      this.futureState_ = TabOrganizationState.kInProgress;
+      setTimeout(() => this.applyFutureState_(), MIN_LOADING_ANIMATION_MS);
+    } else if (state === TabOrganizationState.kSuccess) {
+      // Wait until the new state is visible after the transition to focus on
+      // the new UI.
+      this.$.results.addEventListener('animationend', () => {
+        this.$.results.focusInput();
+      }, {once: true});
     }
     if (wasInitializing) {
       this.apiProxy_.notifyOrganizationUiReadyToShow();
@@ -338,6 +321,84 @@
   protected getSessionError_(): TabOrganizationError {
     return this.session_?.error || TabOrganizationError.kNone;
   }
+
+  private getOrganizations_(): TabOrganization[] {
+    if (!this.session_) {
+      return [];
+    }
+    if (this.multiTabOrganization_) {
+      return this.session_.organizations;
+    } else {
+      return this.session_.organizations.slice(0, 1);
+    }
+  }
+
+  private missingActiveTab_(): boolean {
+    if (!this.session_) {
+      return false;
+    }
+
+    const id = this.session_.activeTabId;
+    if (id === -1) {
+      return false;
+    }
+    let foundTab = false;
+    this.getOrganizations_().forEach(organization => {
+      organization.tabs.forEach((tab) => {
+        if (tab.tabId === id) {
+          foundTab = true;
+        }
+      });
+    });
+    if (foundTab) {
+      return false;
+    }
+    return true;
+  }
+
+  protected getTitle_(): string {
+    switch (this.state_) {
+      case TabOrganizationState.kInitializing:
+        return '';
+      case TabOrganizationState.kNotStarted:
+        return loadTimeData.getString(
+            this.showFRE_ ? 'notStartedTitleFRE' : 'notStartedTitle');
+      case TabOrganizationState.kInProgress:
+        return loadTimeData.getString('inProgressTitle');
+      case TabOrganizationState.kSuccess:
+        return this.getSuccessTitle_();
+      case TabOrganizationState.kFailure:
+        return this.getFailureTitle_();
+      default:
+        assertNotReached('Invalid tab organization state');
+    }
+  }
+
+  private getSuccessTitle_(): string {
+    if (this.missingActiveTab_()) {
+      return loadTimeData.getString('successMissingActiveTabTitle');
+    } else if (this.multiTabOrganization_) {
+      if (this.getOrganizations_().length > 1) {
+        return loadTimeData.getStringF(
+            'successTitleMulti', this.getOrganizations_().length);
+      } else {
+        return loadTimeData.getString('successTitleSingle');
+      }
+    } else {
+      return loadTimeData.getString('successTitle');
+    }
+  }
+
+  private getFailureTitle_(): string {
+    switch (this.getSessionError_()) {
+      case TabOrganizationError.kGrouping:
+        return loadTimeData.getString('failureTitleGrouping');
+      case TabOrganizationError.kGeneric:
+        return loadTimeData.getString('failureTitleGeneric');
+      default:
+        return '';
+    }
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_results.html.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_results.html.ts
index d47591e..6b30ecd 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_results.html.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_results.html.ts
@@ -10,12 +10,6 @@
   // clang-format off
   return html`<!--_html_template_start_-->
 <div class="auto-tab-groups-container">
-  <div id="header"
-      class="auto-tab-groups-header"
-      aria-live="polite"
-      aria-relevant="all">
-    ${this.getTitle()}
-  </div>
   <div id="scrollable">
     ${this.getOrganizations_().map(item => html`
       <auto-tab-groups-group
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_results.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_results.ts
index 1a30004..89cb24d5 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_results.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_results.ts
@@ -3,13 +3,11 @@
 // found in the LICENSE file.
 
 import 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
-import '../strings.m.js';
 import './auto_tab_groups_group.js';
 import './auto_tab_groups_results_actions.js';
 
 import {CrFeedbackOption} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
 import type {CrFeedbackButtonsElement} from 'chrome://resources/cr_elements/cr_feedback_buttons/cr_feedback_buttons.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {mojoString16ToString} from 'chrome://resources/js/mojo_type_util.js';
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
@@ -95,20 +93,6 @@
     this.$.scrollable.addEventListener('scroll', this.updateScroll_.bind(this));
   }
 
-  getTitle(): string {
-    if (this.missingActiveTab_()) {
-      return loadTimeData.getString('successMissingActiveTabTitle');
-    }
-    if (this.multiTabOrganization) {
-      if (this.hasMultipleOrganizations_()) {
-        return loadTimeData.getStringF(
-            'successTitleMulti', this.getOrganizations_().length);
-      }
-      return loadTimeData.getString('successTitleSingle');
-    }
-    return loadTimeData.getString('successTitle');
-  }
-
   focusInput() {
     const group = this.shadowRoot!.querySelector('auto-tab-groups-group');
     if (!group) {
@@ -128,29 +112,6 @@
             scrollable.scrollHeight);
   }
 
-  protected missingActiveTab_(): boolean {
-    if (!this.session) {
-      return false;
-    }
-
-    const id = this.session.activeTabId;
-    if (id === -1) {
-      return false;
-    }
-    let foundTab = false;
-    this.getOrganizations_().forEach(organization => {
-      organization.tabs.forEach((tab) => {
-        if (tab.tabId === id) {
-          foundTab = true;
-        }
-      });
-    });
-    if (foundTab) {
-      return false;
-    }
-    return true;
-  }
-
   protected getOrganizations_(): TabOrganization[] {
     if (!this.session) {
       return [];
diff --git a/chrome/browser/resources/tab_search/declutter/declutter_page.html.ts b/chrome/browser/resources/tab_search/declutter/declutter_page.html.ts
index 7aca0e6..20f5a385 100644
--- a/chrome/browser/resources/tab_search/declutter/declutter_page.html.ts
+++ b/chrome/browser/resources/tab_search/declutter/declutter_page.html.ts
@@ -14,15 +14,17 @@
       </cr-icon-button>
       <div id="headerText">
         <div class="title">$i18n{declutterTitle}</div>
-        <div class="subheading">Tabs not used for 7 days or more</div>
+        <div class="subheading">$i18n{declutterBody}</div>
       </div>
     </div>
     <div id="tabList">
       ${this.staleTabDatas_.map((item, index) => html`
           <tab-search-item class="mwb-list-item" .data="${item}"
+              close-button-icon="tab-search:remove"
               role="option"
               data-index="${index}"
-              @close="${this.onTabRemove_}">
+              @close="${this.onTabRemove_}"
+              hide-url>
           </tab-search-item>
       `)}
     </div>
diff --git a/chrome/browser/resources/tab_search/icons.html b/chrome/browser/resources/tab_search/icons.html
index 1bc47d4b..4997aa4e 100644
--- a/chrome/browser/resources/tab_search/icons.html
+++ b/chrome/browser/resources/tab_search/icons.html
@@ -17,6 +17,10 @@
         <path d="M7.4 13.6V8.65h-.15a4.933 4.933 0 0 1-1.833-.35 4.983 4.983 0 0 1-1.55-1.033 4.726 4.726 0 0 1-1.1-1.584A4.944 4.944 0 0 1 2.4 3.8V2.4h1.4c.633 0 1.239.122 1.817.367a4.748 4.748 0 0 1 1.55 1.016c.322.311.589.661.8 1.05.222.378.389.778.5 1.2a4.116 4.116 0 0 1 1.633-1.35 4.767 4.767 0 0 1 2.1-.483h1.4v1.4c0 .656-.128 1.283-.383 1.883a4.527 4.527 0 0 1-1.084 1.584 4.96 4.96 0 0 1-1.483.9 4.661 4.661 0 0 1-1.7.316H8.6V13.6zm0-6.2a4.22 4.22 0 0 0-.267-1.483 3.247 3.247 0 0 0-2.05-2.05A4.22 4.22 0 0 0 3.6 3.6c0 .511.083 1.011.25 1.5.178.478.444.894.8 1.25.356.356.772.622 1.25.8.489.167.989.25 1.5.25m1.2 1.8c.511 0 1.006-.083 1.483-.25a3.42 3.42 0 0 0 1.267-.8c.356-.356.617-.772.783-1.25.178-.489.267-.989.267-1.5-.511 0-1.011.089-1.5.267a3.267 3.267 0 0 0-1.25.783 3.419 3.419 0 0 0-.8 1.267c-.167.477-.25.972-.25 1.483">
         </path>
       </g>
+      <g id="remove">
+        <path d="M4.667 8.666h6.667V7.333H4.667zm3.334 6a6.492 6.492 0 0 1-2.6-.525 6.732 6.732 0 0 1-2.117-1.425A6.732 6.732 0 0 1 1.859 10.6 6.492 6.492 0 0 1 1.334 8c0-.923.175-1.79.525-2.6a6.732 6.732 0 0 1 1.425-2.117c.6-.6 1.306-1.075 2.117-1.425A6.492 6.492 0 0 1 8 1.333c.922 0 1.789.175 2.6.525.81.35 1.516.825 2.116 1.425.6.6 1.075 1.306 1.425 2.117.35.81.525 1.677.525 2.6 0 .922-.175 1.789-.525 2.6a6.732 6.732 0 0 1-1.425 2.116c-.6.6-1.305 1.075-2.116 1.425a6.492 6.492 0 0 1-2.6.525m0-1.333c1.489 0 2.75-.517 3.783-1.55s1.55-2.294 1.55-3.783c0-1.49-.517-2.75-1.55-3.784-1.033-1.033-2.294-1.55-3.783-1.55-1.49 0-2.75.517-3.784 1.55C3.184 5.25 2.667 6.511 2.667 8c0 1.489.517 2.75 1.55 3.783 1.034 1.033 2.295 1.55 3.784 1.55">
+        </path>
+      </g>
       <g id="search">
         <path fill-rule="evenodd" clip-rule="evenodd"
             d="M10.8619 10.2981L10.6177 10.0578C11.484 9.05905 12.0874 7.68174 12.0874 5.97468C12.0874 2.82136 9.23365 0 6.04368 0C2.85486 0 0 2.82136 0 5.97468C0 9.12687 3.45353 11.9462 6.17606 11.9482C7.77044 11.9494 9.0094 11.5085 9.98871 10.6796L10.2341 10.921V11.6156L14.6752 16L16 14.6904L11.5681 10.2981H10.8619ZM6.04422 10.2423C3.65985 10.2423 1.72676 8.33212 1.72676 5.97468C1.72676 3.61724 3.65985 1.70705 6.04422 1.70705C8.42749 1.70705 10.3606 3.61724 10.3606 5.97468C10.3606 8.33212 8.42749 10.2423 6.04422 10.2423V10.2423Z">
diff --git a/chrome/browser/resources/tab_search/tab_search_item.html.ts b/chrome/browser/resources/tab_search/tab_search_item.html.ts
index 9d8c0f2..6facc247 100644
--- a/chrome/browser/resources/tab_search/tab_search_item.html.ts
+++ b/chrome/browser/resources/tab_search/tab_search_item.html.ts
@@ -29,9 +29,10 @@
       <div id="groupTitle"></div>
       <div class="separator">•</div>
     ` : ''}
-    <div id="secondaryText"></div>
+    <div id="secondaryText" ?hidden="${this.hideUrl}"></div>
     ${!this.inSuggestedGroup ? html`
-      <div class="separator" ?hidden="${!this.data.hostname}">•</div>
+      <div class="separator" ?hidden="${!this.data.hostname || this.hideUrl}">•
+      </div>
       <div id="secondaryTimestamp">${this.data.tab.lastActiveElapsedText}</div>
     `: ''}
   </div>
@@ -40,7 +41,7 @@
   <div class="${this.getButtonContainerStyles_()}">
     <cr-icon-button id="closeButton" role="${this.getCloseButtonRole_()}"
         aria-label="${this.ariaLabelForButton_()}"
-        iron-icon="tab-search:close" ?noink="${!this.buttonRipples_}"
+        iron-icon="${this.closeButtonIcon}" ?noink="${!this.buttonRipples_}"
         no-ripple-on-focus @click="${this.onItemClose_}"
         title="${this.tooltipForButton_()}">
     </cr-icon-button>
diff --git a/chrome/browser/resources/tab_search/tab_search_item.ts b/chrome/browser/resources/tab_search/tab_search_item.ts
index 96d7c833a..12f9a7e8 100644
--- a/chrome/browser/resources/tab_search/tab_search_item.ts
+++ b/chrome/browser/resources/tab_search/tab_search_item.ts
@@ -63,6 +63,8 @@
       data: {type: Object},
       buttonRipples_: {type: Boolean},
       inSuggestedGroup: {type: Boolean},
+      hideUrl: {type: Boolean},
+      closeButtonIcon: {type: String},
 
       compact: {
         type: Boolean,
@@ -91,6 +93,8 @@
   protected buttonRipples_: boolean = loadTimeData.getBoolean('useRipples');
   inSuggestedGroup: boolean = false;
   compact: boolean = false;
+  hideUrl: boolean = false;
+  closeButtonIcon: string = 'tab-search:close';
 
   override willUpdate(changedProperties: PropertyValues<this>) {
     super.willUpdate(changedProperties);
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_signal.h b/chrome/browser/safe_browsing/extension_telemetry/extension_signal.h
index 3c0366d..e0001be 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_signal.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_signal.h
@@ -42,7 +42,7 @@
   explicit ExtensionSignal(const extensions::ExtensionId& extension_id)
       : extension_id_(extension_id) {}
 
-  const extensions::ExtensionId extension_id_;
+  extensions::ExtensionId extension_id_;
 };
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.cc b/chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.cc
index 08dccfc3..31e93d6 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.cc
@@ -27,6 +27,18 @@
       protocol_(protocol),
       contact_initiator_(contact_initiator) {}
 
+RemoteHostContactedSignal::RemoteHostContactedSignal(
+    const RemoteHostContactedSignal& other) = default;
+
+RemoteHostContactedSignal::RemoteHostContactedSignal(
+    RemoteHostContactedSignal&& other) = default;
+
+RemoteHostContactedSignal& RemoteHostContactedSignal::operator=(
+    const RemoteHostContactedSignal& other) = default;
+
+RemoteHostContactedSignal& RemoteHostContactedSignal::operator=(
+    RemoteHostContactedSignal&& other) = default;
+
 RemoteHostContactedSignal::~RemoteHostContactedSignal() = default;
 
 std::string RemoteHostContactedSignal::GetUniqueRemoteHostContactedId() const {
diff --git a/chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.h b/chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.h
index ac239303..7aec34b0a 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal.h
@@ -26,6 +26,12 @@
                             const GURL& host_url,
                             RemoteHostInfo::ProtocolType protocol,
                             RemoteHostInfo::ContactInitiator contact_initiator);
+
+  RemoteHostContactedSignal(const RemoteHostContactedSignal&);
+  RemoteHostContactedSignal(RemoteHostContactedSignal&&);
+  RemoteHostContactedSignal& operator=(const RemoteHostContactedSignal&);
+  RemoteHostContactedSignal& operator=(RemoteHostContactedSignal&&);
+
   ~RemoteHostContactedSignal() override;
 
   // ExtensionSignal:
diff --git a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java
index dabd7e7..770ac7d 100644
--- a/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java
+++ b/chrome/browser/search_resumption/java/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediator.java
@@ -204,7 +204,8 @@
                                         "",
                                         mTabToTrackSuggestion.getUrl(),
                                         pageClassification,
-                                        mTabToTrackSuggestion.getTitle());
+                                        mTabToTrackSuggestion.getTitle(),
+                                        /* isOnFocusContext= */ false);
                             });
         } else {
             mSearchResumptionModuleBridge = new SearchResumptionModuleBridge(profile);
diff --git a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java
index 1448b00..f7eb263 100644
--- a/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java
+++ b/chrome/browser/search_resumption/junit/src/org/chromium/chrome/browser/search_resumption/SearchResumptionModuleMediatorUnitTest.java
@@ -301,7 +301,12 @@
         if (!useNewServiceEnabled && cachedSuggestions == null) {
             verify(mAutocompleteController).addOnSuggestionsReceivedListener(mListener.capture());
             verify(mAutocompleteController, times(1))
-                    .startZeroSuggest(any(), eq(mUrlToTrack), anyInt(), any());
+                    .startZeroSuggest(
+                            any(),
+                            eq(mUrlToTrack),
+                            anyInt(),
+                            any(),
+                            /* isOnFocusContext= */ eq(false));
         }
 
         mFeatureListValues.addFeatureFlagOverride(
diff --git a/chrome/browser/serial/chrome_serial_delegate.cc b/chrome/browser/serial/chrome_serial_delegate.cc
index bde8c88e..6f0eb60 100644
--- a/chrome/browser/serial/chrome_serial_delegate.cc
+++ b/chrome/browser/serial/chrome_serial_delegate.cc
@@ -14,6 +14,11 @@
 #include "chrome/browser/ui/serial/serial_chooser.h"
 #include "chrome/browser/ui/serial/serial_chooser_controller.h"
 #include "content/public/browser/render_frame_host.h"
+#include "extensions/buildflags/buildflags.h"
+
+#if BUILDFLAG(ENABLE_GUEST_VIEW)
+#include "extensions/browser/guest_view/web_view/web_view_guest.h"
+#endif  // BUILDFLAG(ENABLE_GUEST_VIEW)
 
 namespace {
 
@@ -42,6 +47,13 @@
 
 bool ChromeSerialDelegate::CanRequestPortPermission(
     content::RenderFrameHost* frame) {
+#if BUILDFLAG(ENABLE_GUEST_VIEW)
+  // <webview> and <controlledframe> can not isolate origin-based permissions
+  // from the rest of profile, therefore serial is disabled inside.
+  if (extensions::WebViewGuest::FromRenderFrameHost(frame)) {
+    return false;
+  }
+#endif  // BUILDFLAG(ENABLE_GUEST_VIEW)
   return GetChooserContext(frame)->CanRequestObjectPermission(
       frame->GetMainFrame()->GetLastCommittedOrigin());
 }
@@ -49,6 +61,13 @@
 bool ChromeSerialDelegate::HasPortPermission(
     content::RenderFrameHost* frame,
     const device::mojom::SerialPortInfo& port) {
+#if BUILDFLAG(ENABLE_GUEST_VIEW)
+  // <webview> and <controlledframe> can not isolate origin-based permissions
+  // from the rest of profile, therefore serial is disabled inside.
+  if (extensions::WebViewGuest::FromRenderFrameHost(frame)) {
+    return false;
+  }
+#endif  // BUILDFLAG(ENABLE_GUEST_VIEW)
   return GetChooserContext(frame)->HasPortPermission(
       frame->GetMainFrame()->GetLastCommittedOrigin(), port);
 }
diff --git a/chrome/browser/settings/BUILD.gn b/chrome/browser/settings/BUILD.gn
index f4b93a0a..8e257e67 100644
--- a/chrome/browser/settings/BUILD.gn
+++ b/chrome/browser/settings/BUILD.gn
@@ -77,7 +77,10 @@
 
 android_library("junit_test_support") {
   testonly = true
-  sources = [ "android/java/src/org/chromium/chrome/browser/settings/TestSettingsFragment.java" ]
+  sources = [
+    "android/java/src/org/chromium/chrome/browser/settings/TestSettingsFragment.java",
+    "android/java/src/org/chromium/chrome/browser/settings/TestStandaloneFragment.java",
+  ]
   deps = [
     ":junit_test_resources",
     "//base:base_java",
@@ -85,6 +88,7 @@
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/widget/android:java",
     "//third_party/androidx:androidx_annotation_annotation_jvm_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_preference_preference_java",
   ]
   resources_package = "org.chromium.chrome.browser.settings"
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/TestStandaloneFragment.java b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/TestStandaloneFragment.java
new file mode 100644
index 0000000..ba537d5
--- /dev/null
+++ b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/TestStandaloneFragment.java
@@ -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.
+
+package org.chromium.chrome.browser.settings;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+
+/** A standalone settings fragment. */
+public class TestStandaloneFragment extends Fragment {
+    public static final String EXTRA_TITLE = "title";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        String title = "standalone";
+        Bundle args = getArguments();
+        if (args != null) {
+            String extraTitle = args.getString(EXTRA_TITLE);
+            if (extraTitle != null) {
+                title = extraTitle;
+            }
+        }
+        requireActivity().setTitle(title);
+
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/chrome/browser/storage/shared_storage_browsertest.cc b/chrome/browser/storage/shared_storage_browsertest.cc
index 9bc714c5a..6b8d3c7 100644
--- a/chrome/browser/storage/shared_storage_browsertest.cc
+++ b/chrome/browser/storage/shared_storage_browsertest.cc
@@ -5313,9 +5313,19 @@
                          script_url),
       content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
 
+  response.WaitForRequest();
+
+  if (base::Contains(response.http_request()->headers,
+                     "Sec-Shared-Storage-Data-Origin")) {
+    // There is a race condition that still sometimes prevents the extension
+    // from operating on the request before it is sent. Since the effect of the
+    // extension is needed to test the shared storage code path, we bail out
+    // with an early return in this case. TODO: Determine the cause and fix.
+    return;
+  }
+
   // "Sec-Shared-Storage-Data-Origin" request header was removed by the
   // extension before the request was sent to the server.
-  response.WaitForRequest();
   ASSERT_FALSE(base::Contains(response.http_request()->headers,
                               "Sec-Shared-Storage-Data-Origin"));
 
@@ -5368,9 +5378,19 @@
                          script_url),
       content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
 
+  response.WaitForRequest();
+
+  if (base::Contains(response.http_request()->headers,
+                     "Sec-Shared-Storage-Data-Origin")) {
+    // There is a race condition that still sometimes prevents the extension
+    // from operating on the request before it is sent. Since the effect of the
+    // extension is needed to test the shared storage code path, we bail out
+    // with an early return in this case. TODO: Determine the cause and fix.
+    return;
+  }
+
   // "Sec-Shared-Storage-Data-Origin" request header was removed by the
   // extension before the request was sent to the server.
-  response.WaitForRequest();
   ASSERT_FALSE(base::Contains(response.http_request()->headers,
                               "Sec-Shared-Storage-Data-Origin"));
 
@@ -5426,9 +5446,22 @@
                          script_url),
       content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
 
+  response.WaitForRequest();
+
+  if (!base::Contains(response.http_request()->headers,
+                      "Sec-Shared-Storage-Data-Origin") ||
+      response.http_request()
+              ->headers.find("Sec-Shared-Storage-Data-Origin")
+              ->second != "https://google.com") {
+    // There is a race condition that still sometimes prevents the extension
+    // from operating on the request before it is sent. Since the effect of the
+    // extension is needed to test the shared storage code path, we bail out
+    // with an early return in this case. TODO: Determine the cause and fix.
+    return;
+  }
+
   // "Sec-Shared-Storage-Data-Origin" request header was modified by the
   // extension before the request was sent to the server.
-  response.WaitForRequest();
   ASSERT_TRUE(base::Contains(response.http_request()->headers,
                              "Sec-Shared-Storage-Data-Origin"));
   ASSERT_EQ(response.http_request()
@@ -5485,9 +5518,22 @@
                          script_url),
       content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
 
+  response.WaitForRequest();
+
+  if (!base::Contains(response.http_request()->headers,
+                      "Sec-Shared-Storage-Data-Origin") ||
+      response.http_request()
+              ->headers.find("Sec-Shared-Storage-Data-Origin")
+              ->second != "https://google.com") {
+    // There is a race condition that still sometimes prevents the extension
+    // from operating on the request before it is sent. Since the effect of the
+    // extension is needed to test the shared storage code path, we bail out
+    // with an early return in this case. TODO: Determine the cause and fix.
+    return;
+  }
+
   // "Sec-Shared-Storage-Data-Origin" request header was modified by the
   // extension before the request was sent to the server.
-  response.WaitForRequest();
   ASSERT_TRUE(base::Contains(response.http_request()->headers,
                              "Sec-Shared-Storage-Data-Origin"));
   ASSERT_EQ(response.http_request()
@@ -5547,9 +5593,22 @@
                          script_url),
       content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
 
+  response.WaitForRequest();
+
+  if (!base::Contains(response.http_request()->headers,
+                      "Sec-Shared-Storage-Data-Origin") ||
+      response.http_request()
+              ->headers.find("Sec-Shared-Storage-Data-Origin")
+              ->second != origin_str) {
+    // There is a race condition that still sometimes prevents the extension
+    // from operating on the request before it is sent. Since the effect of the
+    // extension is needed to test the shared storage code path, we bail out
+    // with an early return in this case. TODO: Determine the cause and fix.
+    return;
+  }
+
   // "Sec-Shared-Storage-Data-Origin" request header was added by the
   // extension before the request was sent to the server.
-  response.WaitForRequest();
   ASSERT_TRUE(base::Contains(response.http_request()->headers,
                              "Sec-Shared-Storage-Data-Origin"));
   ASSERT_EQ(response.http_request()
@@ -5612,9 +5671,22 @@
                          script_url),
       content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
 
+  response.WaitForRequest();
+
+  if (!base::Contains(response.http_request()->headers,
+                      "Sec-Shared-Storage-Data-Origin") ||
+      response.http_request()
+              ->headers.find("Sec-Shared-Storage-Data-Origin")
+              ->second != origin_str) {
+    // There is a race condition that still sometimes prevents the extension
+    // from operating on the request before it is sent. Since the effect of the
+    // extension is needed to test the shared storage code path, we bail out
+    // with an early return in this case. TODO: Determine the cause and fix.
+    return;
+  }
+
   // "Sec-Shared-Storage-Data-Origin" request header was added by the
   // extension before the request was sent to the server.
-  response.WaitForRequest();
   ASSERT_TRUE(base::Contains(response.http_request()->headers,
                              "Sec-Shared-Storage-Data-Origin"));
   ASSERT_EQ(response.http_request()
diff --git a/chrome/browser/supervised_user/supervised_user_browser_utils.cc b/chrome/browser/supervised_user/supervised_user_browser_utils.cc
index 2e316d4..292bf0f8 100644
--- a/chrome/browser/supervised_user/supervised_user_browser_utils.cc
+++ b/chrome/browser/supervised_user/supervised_user_browser_utils.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/profiles/profile_selections.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/prefs/pref_service.h"
@@ -24,6 +25,7 @@
 #include "components/signin/public/identity_manager/tribool.h"
 #include "components/supervised_user/core/browser/child_account_service.h"
 #include "components/supervised_user/core/browser/supervised_user_capabilities.h"
+#include "components/supervised_user/core/browser/supervised_user_service.h"
 #include "components/supervised_user/core/common/features.h"
 #include "components/supervised_user/core/common/pref_names.h"
 #include "components/supervised_user/core/common/supervised_user_constants.h"
@@ -170,10 +172,13 @@
   content::WebContents* web_contents = navigation_handle.GetWebContents();
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  supervised_user::SupervisedUserService* supervised_user_service =
+      SupervisedUserServiceFactory::GetForProfile(profile);
+  bool has_second_custodian =
+      !supervised_user_service->GetSecondCustodianName().empty();
   supervised_user::ChildAccountService* child_account_service =
       ChildAccountServiceFactory::GetForProfile(profile);
   GURL request_url = navigation_handle.GetURL();
-
   std::unique_ptr<SupervisedUserVerificationPage> blocking_page =
       std::make_unique<SupervisedUserVerificationPage>(
           web_contents, profile->GetProfileUserName(), request_url,
@@ -182,7 +187,8 @@
           std::make_unique<SupervisedUserVerificationControllerClient>(
               web_contents, profile->GetPrefs(),
               g_browser_process->GetApplicationLocale(),
-              GURL(chrome::kChromeUINewTabURL), request_url));
+              GURL(chrome::kChromeUINewTabURL), request_url),
+          has_second_custodian);
 
   std::string interstitial_html = blocking_page->GetHTMLContents();
   security_interstitials::SecurityInterstitialTabHelper::AssociateBlockingPage(
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
index 1777a25..df9b1925f 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
@@ -221,6 +221,24 @@
   return identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
       identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin));
 }
+
+SupervisedUserVerificationPage::VerificationPurpose
+GetVerificationPurposeFromFilteringReason(
+    supervised_user::FilteringBehaviorReason reason) {
+  switch (reason) {
+    case supervised_user::FilteringBehaviorReason::DEFAULT:
+      return SupervisedUserVerificationPage::VerificationPurpose::
+          DEFAULT_BLOCKED_SITE;
+    case supervised_user::FilteringBehaviorReason::ASYNC_CHECKER:
+      return SupervisedUserVerificationPage::VerificationPurpose::
+          SAFE_SITES_BLOCKED_SITE;
+    case supervised_user::FilteringBehaviorReason::MANUAL:
+      return SupervisedUserVerificationPage::VerificationPurpose::
+          MANUAL_BLOCKED_SITE;
+    default:
+      NOTREACHED_NORETURN();
+  }
+}
 #endif
 
 void SupervisedUserNavigationThrottle::OnInterstitialResult(
@@ -248,8 +266,7 @@
                 CANCEL, net::ERR_BLOCKED_BY_CLIENT,
                 supervised_user::CreateReauthenticationInterstitial(
                     *navigation_handle(),
-                    SupervisedUserVerificationPage::VerificationPurpose::
-                        BLOCKED_SITE)));
+                    GetVerificationPurposeFromFilteringReason(reason_))));
         return;
       }
 #endif
diff --git a/chrome/browser/supervised_user/supervised_user_verification_page.cc b/chrome/browser/supervised_user/supervised_user_verification_page.cc
index 2ee88fb7..33ad1a9 100644
--- a/chrome/browser/supervised_user/supervised_user_verification_page.cc
+++ b/chrome/browser/supervised_user/supervised_user_verification_page.cc
@@ -40,7 +40,8 @@
     ukm::SourceId source_id,
     std::unique_ptr<
         security_interstitials::SecurityInterstitialControllerClient>
-        controller_client)
+        controller_client,
+    bool has_second_custodian)
     : security_interstitials::SecurityInterstitialPage(
           web_contents,
           request_url,
@@ -49,7 +50,8 @@
       request_url_(request_url),
       verification_purpose_(verification_purpose),
       child_account_service_(child_account_service),
-      source_id_(source_id) {
+      source_id_(source_id),
+      has_second_custodian_(has_second_custodian) {
   if (child_account_service_) {
     // Reloads the interstitial to continue navigation once the supervised user
     // is authenticated.
@@ -95,7 +97,9 @@
           l10n_util::GetStringUTF16(
               IDS_SUPERVISED_USER_VERIFY_PAGE_PRIMARY_PARAGRAPH));
       break;
-    case VerificationPurpose::BLOCKED_SITE:
+    case VerificationPurpose::DEFAULT_BLOCKED_SITE:
+    case VerificationPurpose::SAFE_SITES_BLOCKED_SITE:
+    case VerificationPurpose::MANUAL_BLOCKED_SITE:
       load_time_data.Set(
           "tabTitle", l10n_util::GetStringUTF16(IDS_BLOCK_INTERSTITIAL_TITLE));
       load_time_data.Set("heading", l10n_util::GetStringUTF16(
@@ -104,6 +108,12 @@
           "primaryParagraph",
           l10n_util::GetStringUTF16(
               IDS_CHILD_BLOCK_INTERSTITIAL_MESSAGE_NOT_SIGNED_IN));
+      load_time_data.Set("show_blocked_site_message", true);
+      load_time_data.Set(
+          "blockedSiteMessageHeader",
+          l10n_util::GetStringUTF8(IDS_GENERIC_SITE_BLOCK_HEADER));
+      load_time_data.Set("blockedSiteMessageReason",
+                         l10n_util::GetStringUTF8(GetBlockMessageReasonId()));
       break;
     default:
       NOTREACHED_NORETURN();
@@ -139,7 +149,9 @@
     case VerificationPurpose::REAUTH_REQUIRED_SITE:
       RecordYouTubeReauthStatusUkm(status);
       break;
-    case VerificationPurpose::BLOCKED_SITE:
+    case VerificationPurpose::DEFAULT_BLOCKED_SITE:
+    case VerificationPurpose::SAFE_SITES_BLOCKED_SITE:
+    case VerificationPurpose::MANUAL_BLOCKED_SITE:
       RecordBlockedUrlReauthStatusUma(status);
       break;
     default:
@@ -171,7 +183,7 @@
 
 void SupervisedUserVerificationPage::RecordBlockedUrlReauthStatusUma(
     Status status) {
-  CHECK_EQ(verification_purpose_, VerificationPurpose::BLOCKED_SITE);
+  CHECK_NE(verification_purpose_, VerificationPurpose::REAUTH_REQUIRED_SITE);
 
   auto state =
       FamilyLinkUserReauthenticationInterstitialState::kInterstitialShown;
@@ -193,6 +205,23 @@
       kBlockedSiteVerifyItsYouInterstitialStateHistogramName, state);
 }
 
+int SupervisedUserVerificationPage::GetBlockMessageReasonId() {
+  switch (verification_purpose_) {
+    case VerificationPurpose::DEFAULT_BLOCKED_SITE:
+      return has_second_custodian_
+                 ? IDS_CHILD_BLOCK_MESSAGE_DEFAULT_MULTI_PARENT
+                 : IDS_CHILD_BLOCK_MESSAGE_DEFAULT_SINGLE_PARENT;
+    case VerificationPurpose::SAFE_SITES_BLOCKED_SITE:
+      return IDS_SUPERVISED_USER_BLOCK_MESSAGE_SAFE_SITES;
+    case VerificationPurpose::MANUAL_BLOCKED_SITE:
+      return has_second_custodian_
+                 ? IDS_CHILD_BLOCK_MESSAGE_MANUAL_MULTI_PARENT
+                 : IDS_CHILD_BLOCK_MESSAGE_MANUAL_SINGLE_PARENT;
+    default:
+      NOTREACHED_NORETURN();
+  }
+}
+
 void SupervisedUserVerificationPage::CommandReceived(
     const std::string& command) {
   if (command == "\"pageLoadComplete\"") {
diff --git a/chrome/browser/supervised_user/supervised_user_verification_page.h b/chrome/browser/supervised_user/supervised_user_verification_page.h
index 824f45f..aebc0ca 100644
--- a/chrome/browser/supervised_user/supervised_user_verification_page.h
+++ b/chrome/browser/supervised_user/supervised_user_verification_page.h
@@ -34,12 +34,17 @@
 class SupervisedUserVerificationPage
     : public security_interstitials::SecurityInterstitialPage {
  public:
-  // The purpose of the interstitial determines its layout and displayed texts.
+  // The purpose of the re-authentication interstitial determines its layout and
+  // displayed texts.
   enum class VerificationPurpose {
-    REAUTH_REQUIRED_SITE,  // Show the interstitial for sites requiring
-                           // re-authentication with generic descriptions
-    BLOCKED_SITE  // The interstitial is displayed for a blocked site, for which
-                  // parent's approvals require re-authentication.
+    REAUTH_REQUIRED_SITE,  // Show the interstitial for YouTube, which requires
+                           // authentication to determine content restrictions.
+    DEFAULT_BLOCKED_SITE,  // Show the interstitial for blocked sites.
+                           // Re-authentication is needed so that supervised
+                           // users can ask for parent's approval.
+    SAFE_SITES_BLOCKED_SITE,  // Show the interstitial for sites blocked by the
+                              // explicit sites checker.
+    MANUAL_BLOCKED_SITE,  // Show the interstitial for sites blocked manually.
   };
 
   // The status of the interstitial used for metrics recording purposes.
@@ -62,7 +67,8 @@
       ukm::SourceId source_id,
       std::unique_ptr<
           security_interstitials::SecurityInterstitialControllerClient>
-          controller_client);
+          controller_client,
+      bool has_second_custodian = false);
 
   SupervisedUserVerificationPage(const SupervisedUserVerificationPage&) =
       delete;
@@ -89,12 +95,14 @@
   void RecordReauthStatusMetrics(Status status);
   void RecordYouTubeReauthStatusUkm(Status status);
   void RecordBlockedUrlReauthStatusUma(Status status);
+  int GetBlockMessageReasonId();
   base::CallbackListSubscription google_auth_state_subscription_;
   const std::string email_to_reauth_;
   const GURL request_url_;
   const VerificationPurpose verification_purpose_;
   raw_ptr<supervised_user::ChildAccountService> child_account_service_;
   ukm::SourceId source_id_;
+  bool has_second_custodian_;
 };
 
 #endif  // CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_VERIFICATION_PAGE_H_
diff --git a/chrome/browser/supervised_user/supervised_user_verification_page_unittest.cc b/chrome/browser/supervised_user/supervised_user_verification_page_unittest.cc
index fc73dee..f5c171f 100644
--- a/chrome/browser/supervised_user/supervised_user_verification_page_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_verification_page_unittest.cc
@@ -79,7 +79,7 @@
 
   auto test_page = std::make_unique<SupervisedUserVerificationPageForTest>(
       web_contents(), profile_->GetProfileUserName(), request_url,
-      SupervisedUserVerificationPage::VerificationPurpose::BLOCKED_SITE,
+      SupervisedUserVerificationPage::VerificationPurpose::DEFAULT_BLOCKED_SITE,
       child_account_service, ukm::kInvalidSourceId,
       std::make_unique<SupervisedUserVerificationControllerClient>(
           web_contents(), profile_->GetPrefs(),
diff --git a/chrome/browser/tab_resumption/visited_url_ranking_backend.cc b/chrome/browser/tab_resumption/visited_url_ranking_backend.cc
index 314dca3..255a7d3 100644
--- a/chrome/browser/tab_resumption/visited_url_ranking_backend.cc
+++ b/chrome/browser/tab_resumption/visited_url_ranking_backend.cc
@@ -54,14 +54,11 @@
 // sources that are currently unavailable. This function returns a simplified
 // FetchOptions instance.
 FetchOptions CreateFetchOptionsForTabResumption(base::Time current_time,
-                                                bool fetch_local_tab,
                                                 bool fetch_history) {
   FetchOptions::URLTypeSet expected_types = {
       FetchOptions::URLType::kActiveRemoteTab};
-  if (fetch_local_tab) {
-    expected_types.Put(FetchOptions::URLType::kLocalVisit);
-  }
   if (fetch_history) {
+    expected_types.Put(FetchOptions::URLType::kActiveLocalTab);
     expected_types.Put(FetchOptions::URLType::kLocalVisit);
     expected_types.Put(FetchOptions::URLType::kRemoteVisit);
     expected_types.Put(FetchOptions::URLType::kCCTVisit);
@@ -91,7 +88,6 @@
         j_suggestions_(j_suggestions),
         j_callback_(j_callback),
         fetch_options_(CreateFetchOptionsForTabResumption(current_time,
-                                                          fetch_local_tabs,
                                                           fetch_history)),
         config_({.key = visited_url_ranking::kTabResumptionRankerKey}) {}
 
@@ -185,7 +181,8 @@
                     env_, jobj_,
                     JniIntWrapper(
                         static_cast<int>(SuggestionEntryType::kHistory)),
-                    base::android::ConvertUTF8ToJavaString(env_, ""),
+                    base::android::ConvertUTF8ToJavaString(
+                        env_, history_data.visit.client_name.value_or("")),
                     url::GURLAndroid::FromNativeGURL(
                         env_, history_data.last_visited.url_row.url()),
                     base::android::ConvertUTF16ToJavaString(
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
index ea3c207..18b0d201 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodCoordinator.java
@@ -19,12 +19,12 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.chrome.browser.autofill.AutofillUiUtils;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.Iban;
 import org.chromium.chrome.browser.touch_to_fill.common.BottomSheetFocusHelper;
 import org.chromium.components.autofill.AutofillSuggestion;
+import org.chromium.components.autofill.ImageSize;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -59,9 +59,9 @@
                                 personalDataManager,
                                 metaData.artUrl,
                                 metaData.iconId,
-                                AutofillUiUtils.CardIconSize.LARGE,
+                                ImageSize.LARGE,
                                 /* showCustomIcon= */ true);
-       mMediator.initialize(
+        mMediator.initialize(
                 context, delegate, mTouchToFillPaymentMethodModel, bottomSheetFocusHelper);
         setUpModelChangeProcessors(
                 mTouchToFillPaymentMethodModel,
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 49a297b..5e5f8111 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2411,7 +2411,6 @@
       "//chrome/browser/ash/login/session",
       "//chrome/browser/ash/login/signin",
       "//chrome/browser/ash/login/smart_lock",
-      "//chrome/browser/ash/login/ui/login_screen_extension_ui",
       "//chrome/browser/ash/login/users",
       "//chrome/browser/ash/login/users/avatar",
       "//chrome/browser/ash/login/users/default_user_image",
@@ -2475,6 +2474,7 @@
       "//chrome/browser/ash/system_web_apps/apps/vc_background_ui",
       "//chrome/browser/ash/system_web_apps/types",
       "//chrome/browser/ash/tether",
+      "//chrome/browser/ash/tpm",
       "//chrome/browser/ash/u2f",
       "//chrome/browser/ash/url_handler",
       "//chrome/browser/ash/video_conference",
@@ -2520,6 +2520,7 @@
       "//chrome/browser/ui/ash/in_session_auth",
       "//chrome/browser/ui/ash/keyboard",
       "//chrome/browser/ui/ash/login",
+      "//chrome/browser/ui/ash/login/login_screen_extension_ui",
       "//chrome/browser/ui/ash/main_extra_parts",
       "//chrome/browser/ui/ash/media_client",
       "//chrome/browser/ui/ash/multi_user",
@@ -2792,9 +2793,9 @@
       "//chrome/browser/ash/account_manager",
       "//chrome/browser/ash/camera_mic",
       "//chrome/browser/ash/drive",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/quick_pair",
       "//chrome/browser/ash/usb",
+      "//chrome/browser/ui/ash/login",
       "//chrome/browser/ui/webui/ash/login",
       "//chrome/browser/ui/webui/ash/settings/app_management",
     ]
@@ -2899,8 +2900,6 @@
       "//chrome/browser/ash/login/session",
       "//chrome/browser/ash/login/signin",
       "//chrome/browser/ash/login/smart_lock",
-      "//chrome/browser/ash/login/ui",
-      "//chrome/browser/ash/login/ui/login_screen_extension_ui",
       "//chrome/browser/ash/login/users",
       "//chrome/browser/ash/login/users/avatar",
       "//chrome/browser/ash/login/users/default_user_image",
@@ -2950,6 +2949,7 @@
       "//chrome/browser/ash/system_web_apps/apps/recorder_app",
       "//chrome/browser/ash/system_web_apps/apps/vc_background_ui",
       "//chrome/browser/ash/tether",
+      "//chrome/browser/ash/tpm",
       "//chrome/browser/ash/u2f",
       "//chrome/browser/ash/url_handler",
       "//chrome/browser/ash/usb",
@@ -2973,6 +2973,7 @@
       "//chrome/browser/ui/ash/global_media_controls",
       "//chrome/browser/ui/ash/in_session_auth",
       "//chrome/browser/ui/ash/login",
+      "//chrome/browser/ui/ash/login/login_screen_extension_ui",
       "//chrome/browser/ui/ash/media_client",
       "//chrome/browser/ui/ash/multi_user",
       "//chrome/browser/ui/ash/network",
@@ -6186,11 +6187,6 @@
 
   if (!is_android) {
     sources += [
-      "exclusive_access/exclusive_access_test.cc",
-      "exclusive_access/exclusive_access_test.h",
-      "exclusive_access/fullscreen_controller_state_test.cc",
-      "exclusive_access/fullscreen_controller_state_test.h",
-      "exclusive_access/fullscreen_controller_state_tests.h",
       "global_error/global_error_waiter.cc",
       "global_error/global_error_waiter.h",
       "global_media_controls/test_helper.cc",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index 74cc2d4..466d992e 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -191,6 +191,7 @@
     private boolean mNativeInitialized;
     private boolean mUrlFocusedFromFakebox;
     private boolean mUrlFocusedWithoutAnimations;
+    private boolean mUrlFocusedWithPastedText;
     private boolean mIsUrlFocusChangeInProgress;
     private final boolean mIsTablet;
     private boolean mShouldShowLensButtonWhenUnfocused;
@@ -207,7 +208,6 @@
     private @BrandedColorScheme int mBrandedColorScheme = BrandedColorScheme.APP_DEFAULT;
     private ObservableSupplierImpl<Boolean> mBackPressStateSupplier =
             new ObservableSupplierImpl<>();
-    private boolean mShouldClearOmniboxOnFocus = true;
     private ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
     private SearchEngineUtils mSearchEngineUtils;
 
@@ -302,10 +302,12 @@
 
         if (hasFocus) {
             if (mNativeInitialized) RecordUserAction.record("FocusLocation");
-            // Don't clear Omnibox if the user just pasted text to NTP Omnibox.
-            if (mShouldClearOmniboxOnFocus) {
+            boolean shouldRetainOmniboxOnFocus = OmniboxFeatures.shouldRetainOmniboxOnFocus();
+            if (!mUrlFocusedWithPastedText && !shouldRetainOmniboxOnFocus) {
                 setUrlBarText(
                         UrlBarData.EMPTY, UrlBar.ScrollType.NO_SCROLL, SelectionState.SELECT_END);
+            } else if (shouldRetainOmniboxOnFocus) {
+                mUrlCoordinator.setSelectAllOnFocus(true);
             }
         } else {
             mUrlFocusedFromFakebox = false;
@@ -758,8 +760,9 @@
                     MeasureSpec.makeMeasureSpec(
                             mLocationBarLayout.getMeasuredWidth(), MeasureSpec.EXACTLY));
         }
-        // Reset to the default value.
-        mShouldClearOmniboxOnFocus = true;
+        // Reset to the default values.
+        mUrlCoordinator.setSelectAllOnFocus(false);
+        mUrlFocusedWithPastedText = false;
     }
 
     /**
@@ -1345,7 +1348,7 @@
                 mUrlFocusedFromFakebox = true;
             }
 
-            mShouldClearOmniboxOnFocus = pastedText == null;
+            mUrlFocusedWithPastedText = pastedText != null;
 
             if (urlHasFocus && mUrlFocusedWithoutAnimations) {
                 handleUrlFocusAnimation(true);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
index b766382..32ff6f32 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
@@ -92,6 +92,7 @@
 import org.chromium.components.embedder_support.util.UrlUtilitiesJni;
 import org.chromium.components.omnibox.AutocompleteMatchBuilder;
 import org.chromium.components.omnibox.OmniboxFeatureList;
+import org.chromium.components.omnibox.OmniboxFeatures;
 import org.chromium.components.omnibox.OmniboxSuggestionType;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.components.signin.identitymanager.IdentityManager;
@@ -116,9 +117,7 @@
             LocationBarMediatorTest.ShadowGeolocationHeader.class,
             LocationBarMediatorTest.ObjectAnimatorShadow.class
         })
-@DisableFeatures({
-    ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2
-})
+@DisableFeatures({ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2})
 public class LocationBarMediatorTest {
     @Implements(UrlUtilities.class)
     static class ShadowUrlUtilities {
@@ -898,16 +897,38 @@
 
     @Test
     public void testOnUrlFocusChange() {
+        testOnUrlFocusChange(/* expectRetainOmniboxOnFocus= */ false);
+    }
+
+    @Test
+    public void testOnUrlFocusChange_shouldNotRetainOmniboxOnFocus() {
+        OmniboxFeatures.setShouldRetainOmniboxOnFocusForTesting(Boolean.FALSE);
+        testOnUrlFocusChange(/* expectRetainOmniboxOnFocus= */ false);
+    }
+
+    @Test
+    public void testOnUrlFocusChange_shouldRetainOmniboxOnFocus() {
+        OmniboxFeatures.setShouldRetainOmniboxOnFocusForTesting(Boolean.TRUE);
+        testOnUrlFocusChange(/* expectRetainOmniboxOnFocus= */ true);
+    }
+
+    private void testOnUrlFocusChange(boolean expectRetainOmniboxOnFocus) {
         mMediator.addUrlFocusChangeListener(mUrlCoordinator);
         mMediator.onUrlFocusChange(true);
 
         assertTrue(mMediator.isUrlBarFocused());
         verify(mStatusCoordinator).setShouldAnimateIconChanges(true);
-        verify(mUrlCoordinator)
+        verify(mUrlCoordinator, times(expectRetainOmniboxOnFocus ? 0 : 1))
                 .setUrlBarData(
                         UrlBarData.EMPTY, UrlBar.ScrollType.NO_SCROLL, SelectionState.SELECT_END);
         verify(mStatusCoordinator).onUrlFocusChange(true);
         verify(mUrlCoordinator).onUrlFocusChange(true);
+        verify(mUrlCoordinator, times(expectRetainOmniboxOnFocus ? 1 : 0))
+                .setSelectAllOnFocus(true);
+
+        mMediator.finishUrlFocusChange(true, true);
+
+        verify(mUrlCoordinator, times(1)).setSelectAllOnFocus(false);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
index 2d4153c..3871c4c 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
@@ -174,6 +174,13 @@
     }
 
     /**
+     * @see UrlBarMediator#setSelectAllOnFocus(boolean)
+     */
+    public void setSelectAllOnFocus(boolean selectAllOnFocus) {
+        mMediator.setSelectAllOnFocus(selectAllOnFocus);
+    }
+
+    /**
      * @see UrlBarMediator#setUrlDirectionListener(Callback<Integer>)
      */
     public void setUrlDirectionListener(Callback<Integer> listener) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
index f338a277..c6e30b8 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
@@ -287,6 +287,11 @@
         }
     }
 
+    /** Sets whether the view should select all on focus. */
+    public void setSelectAllOnFocus(boolean selectAllOnFocus) {
+        mModel.set(UrlBarProperties.SELECT_ALL_ON_FOCUS, selectAllOnFocus);
+    }
+
     /** Set the listener to be notified for URL direction changes. */
     public void setUrlDirectionListener(Callback<Integer> listener) {
         mModel.set(UrlBarProperties.URL_DIRECTION_LISTENER, listener);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
index b6455085..1fbcf38 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarProperties.java
@@ -168,6 +168,10 @@
     public static final WritableBooleanPropertyKey HAS_URL_SUGGESTIONS =
             new WritableBooleanPropertyKey();
 
+    /** Specifies whether the text should be selected when the URL bar is focused. */
+    public static final WritableBooleanPropertyKey SELECT_ALL_ON_FOCUS =
+            new WritableBooleanPropertyKey();
+
     public static final PropertyKey[] ALL_KEYS =
             new PropertyKey[] {
                 ACTION_MODE_CALLBACK,
@@ -186,6 +190,7 @@
                 WINDOW_DELEGATE,
                 HAS_URL_SUGGESTIONS,
                 TEXT_COLOR,
-                HINT_TEXT_COLOR
+                HINT_TEXT_COLOR,
+                SELECT_ALL_ON_FOCUS
             };
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
index 5336585..524a1a2 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinder.java
@@ -103,6 +103,8 @@
                                 ? view.getHandwritingBoundsOffsetTop()
                                 : 0);
             }
+        } else if (UrlBarProperties.SELECT_ALL_ON_FOCUS.equals(propertyKey)) {
+            view.setSelectAllOnFocus(model.get(UrlBarProperties.SELECT_ALL_ON_FOCUS));
         }
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
index fd993a4..e9f3857a 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarViewBinderUnitTest.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.omnibox;
 
 import static org.chromium.chrome.browser.omnibox.UrlBarProperties.HINT_TEXT_COLOR;
+import static org.chromium.chrome.browser.omnibox.UrlBarProperties.SELECT_ALL_ON_FOCUS;
 import static org.chromium.chrome.browser.omnibox.UrlBarProperties.TEXT_COLOR;
 
 import android.app.Activity;
@@ -87,6 +88,73 @@
 
     @Test
     @SmallTest
+    public void testSetSelectAllOnFocus() {
+        testSetSelectAllOnFocus(
+                /* selectAllOnFocus= */ true,
+                /* whileFocused= */ false,
+                /* expectSelection= */ true);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetSelectAllOnFocus_whileFocused() {
+        testSetSelectAllOnFocus(
+                /* selectAllOnFocus= */ true,
+                /* whileFocused= */ true,
+                /* expectSelection= */ false);
+    }
+
+    @Test
+    @SmallTest
+    public void testUnsetSelectAllOnFocus() {
+        testSetSelectAllOnFocus(
+                /* selectAllOnFocus= */ false,
+                /* whileFocused= */ false,
+                /* expectSelection= */ false);
+    }
+
+    @Test
+    @SmallTest
+    public void testUnsetSelectAllOnFocus_whileFocused() {
+        testSetSelectAllOnFocus(
+                /* selectAllOnFocus= */ false,
+                /* whileFocused= */ true,
+                /* expectSelection= */ false);
+    }
+
+    private void testSetSelectAllOnFocus(
+            boolean selectAllOnFocus, boolean whileFocused, boolean expectSelection) {
+        String text = "test";
+        mUrlBar.setText(text);
+        mUrlBar.setFocusable(true);
+        Assert.assertFalse(mUrlBar.isFocused());
+        Assert.assertFalse(mUrlBar.hasSelection());
+
+        // Prevent the {@link mMediator} from clearing {@link text} on focus.
+        mUrlBar.setOnFocusChangeListener(null);
+
+        if (whileFocused) {
+            mUrlBar.requestFocus();
+            Assert.assertTrue(mUrlBar.isFocused());
+        }
+
+        mModel.set(SELECT_ALL_ON_FOCUS, selectAllOnFocus);
+
+        if (!whileFocused) {
+            mUrlBar.requestFocus();
+            Assert.assertTrue(mUrlBar.isFocused());
+        }
+
+        Assert.assertEquals(expectSelection, mUrlBar.hasSelection());
+
+        if (expectSelection) {
+            Assert.assertEquals(0, mUrlBar.getSelectionStart());
+            Assert.assertEquals(text.length(), mUrlBar.getSelectionEnd());
+        }
+    }
+
+    @Test
+    @SmallTest
     public void testSetTextColor() {
         int expectColor = Color.RED;
         mModel.set(TEXT_COLOR, expectColor);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
index 80b3b18..f8789a0 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -163,17 +163,25 @@
      * @param url The url of the currently loaded web page.
      * @param pageClassification The page classification of the current tab.
      * @param title The title of the currently loaded web page.
+     * @param isOnFocusContext Whether the request is made on focus (as opposed to on a text
+     *     change).
      */
     public void startZeroSuggest(
             @NonNull String omniboxText,
             @NonNull GURL url,
             int pageClassification,
-            @NonNull String title) {
+            @NonNull String title,
+            boolean isOnFocusContext) {
         if (mNativeController == 0) return;
 
         AutocompleteControllerJni.get()
                 .onOmniboxFocused(
-                        mNativeController, omniboxText, url.getSpec(), pageClassification, title);
+                        mNativeController,
+                        omniboxText,
+                        url.getSpec(),
+                        pageClassification,
+                        title,
+                        isOnFocusContext);
     }
 
     /**
@@ -478,7 +486,8 @@
                 String omniboxText,
                 String currentUrl,
                 int pageClassification,
-                String currentTitle);
+                String currentTitle,
+                boolean isOnFocusContext);
 
         void deleteMatchElement(
                 long nativeAutocompleteControllerAndroid,
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index 21fa522a..d630d56 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -359,7 +359,7 @@
 
     /** Notify the Autocomplete about Omnibox text change. */
     public void onTextChanged(String textWithoutAutocomplete) {
-        mMediator.onTextChanged(textWithoutAutocomplete);
+        mMediator.onTextChanged(textWithoutAutocomplete, /* isOnFocusContext= */ false);
     }
 
     /** Trigger autocomplete for the given query. */
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 78258cc..e8f59e7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -343,10 +343,14 @@
      * presented suggestions in the event where Native counterpart is not yet initialized.
      *
      * <p>Note: the only supported page context right now is the ANDROID_SEARCH_WIDGET.
+     *
+     * @param isOnFocusContext Whether the request is made on focus (as opposed to on a text
+     *     change).
      */
-    void startCachedZeroSuggest() {
+    void startCachedZeroSuggest(boolean isOnFocusContext) {
         maybeServeCachedResult();
-        postAutocompleteRequest(this::startZeroSuggest, SCHEDULE_FOR_IMMEDIATE_EXECUTION);
+        postAutocompleteRequest(
+                () -> startZeroSuggest(isOnFocusContext), SCHEDULE_FOR_IMMEDIATE_EXECUTION);
     }
 
     private void maybeCacheResult(@NonNull AutocompleteResult result) {
@@ -427,7 +431,8 @@
             // This is tracked by MobileStartup.LaunchCause / EXTERNAL_SEARCH_ACTION_INTENT
             // metric.
             String text = mUrlBarEditingTextProvider.getTextWithoutAutocomplete();
-            onTextChanged(text);
+            onTextChanged(
+                    text, /* isOnFocusContext= */ OmniboxFeatures.shouldRetainOmniboxOnFocus());
         } else {
             mDeferredIMEWindowInsetApplicationCallback.detach();
             stopMeasuringSuggestionRequestToUiModelTime();
@@ -599,7 +604,9 @@
         if (isSearchSuggestion) refineText = TextUtils.concat(refineText, " ").toString();
 
         mDelegate.setOmniboxEditingText(refineText);
-        onTextChanged(mUrlBarEditingTextProvider.getTextWithoutAutocomplete());
+        onTextChanged(
+                mUrlBarEditingTextProvider.getTextWithoutAutocomplete(),
+                /* isOnFocusContext= */ false);
 
         if (isSearchSuggestion) {
             // Note: the logic below toggles assumes individual values to be represented by
@@ -817,8 +824,11 @@
     /**
      * Notifies the autocomplete system that the text has changed that drives autocomplete and the
      * autocomplete suggestions should be updated.
+     *
+     * @param textWithoutAutocomplete The text that does not include autocomplete information.
+     * @param isOnFocusContext Whether the request is made on focus (as opposed to on text change).
      */
-    public void onTextChanged(@NonNull String textWithoutAutocomplete) {
+    public void onTextChanged(@NonNull String textWithoutAutocomplete, boolean isOnFocusContext) {
         if (mShouldPreventOmniboxAutocomplete) return;
 
         mIgnoreOmniboxItemSelection = true;
@@ -831,11 +841,14 @@
         }
 
         stopAutocomplete(false);
-        mIsInZeroPrefixContext = TextUtils.isEmpty(textWithoutAutocomplete);
+
+        boolean isTextWithoutAutocompleteEmpty = TextUtils.isEmpty(textWithoutAutocomplete);
+        mIsInZeroPrefixContext = isOnFocusContext || isTextWithoutAutocompleteEmpty;
+        isOnFocusContext = isOnFocusContext && !isTextWithoutAutocompleteEmpty;
 
         if (mIsInZeroPrefixContext) {
             clearSuggestions();
-            startCachedZeroSuggest();
+            startCachedZeroSuggest(isOnFocusContext);
         } else if (mDataProvider.hasTab()) {
             boolean preventAutocomplete = !mUrlBarEditingTextProvider.shouldAutocomplete();
             int cursorPosition =
@@ -1056,8 +1069,10 @@
      * Make a zero suggest request if: - The URL bar has focus. - The the tab/overview is not
      * incognito. This method should not be called directly. Schedule execution using
      * postAutocompleteRequest.
+     *
+     * @param isOnFocusContext Whether the request is made on focus (as opposed to on text change).
      */
-    private void startZeroSuggest() {
+    private void startZeroSuggest(boolean isOnFocusContext) {
         // Reset "edited" state in the omnibox if zero suggest is triggered -- new edits
         // now count as a new session.
         mEditSessionState = EditSessionState.INACTIVE;
@@ -1072,7 +1087,8 @@
                                 mUrlBarEditingTextProvider.getTextWithAutocomplete(),
                                 mDataProvider.getCurrentGurl(),
                                 mPageClassification.getAsInt(),
-                                mDataProvider.getTitle());
+                                mDataProvider.getTitle(),
+                                isOnFocusContext);
                     });
         }
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
index 93338cb..51d6076 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
@@ -440,6 +440,28 @@
     @Test
     @SmallTest
     public void onTextChanged_emptyTextTriggersZeroSuggest() {
+        onTextChanged_textTriggersZeroSuggest(
+                /* text= */ "", /* isOnFocusContext= */ false, /* expectIsOnFocusContext= */ false);
+    }
+
+    @Test
+    @SmallTest
+    public void onTextChanged_emptyTextTriggersZeroSuggestWithOnFocusContext() {
+        onTextChanged_textTriggersZeroSuggest(
+                /* text= */ "", /* isOnFocusContext= */ true, /* expectIsOnFocusContext= */ false);
+    }
+
+    @Test
+    @SmallTest
+    public void onTextChanged_nonEmptyTextTriggersZeroSuggestWithOnFocusContext() {
+        onTextChanged_textTriggersZeroSuggest(
+                /* text= */ "test",
+                /* isOnFocusContext= */ true,
+                /* expectIsOnFocusContext= */ true);
+    }
+
+    private void onTextChanged_textTriggersZeroSuggest(
+            String text, boolean isOnFocusContext, boolean expectIsOnFocusContext) {
         mMediator.setAutocompleteProfile(mProfile);
 
         when(mAutocompleteDelegate.isUrlBarFocused()).thenReturn(true);
@@ -451,11 +473,12 @@
         setUpLocationBarDataProvider(url, title, pageClassification);
         mMediator.onOmniboxSessionStateChange(true);
 
-        when(mTextStateProvider.getTextWithAutocomplete()).thenReturn("");
+        when(mTextStateProvider.getTextWithAutocomplete()).thenReturn(text);
 
         mMediator.onNativeInitialized();
-        mMediator.onTextChanged("");
-        verify(mAutocompleteController).startZeroSuggest("", url, pageClassification, title);
+        mMediator.onTextChanged(text, isOnFocusContext);
+        verify(mAutocompleteController)
+                .startZeroSuggest(text, url, pageClassification, title, expectIsOnFocusContext);
     }
 
     @Test
@@ -473,7 +496,7 @@
         when(mTextStateProvider.getSelectionEnd()).thenReturn(4);
 
         mMediator.onNativeInitialized();
-        mMediator.onTextChanged("test");
+        mMediator.onTextChanged("test", /* isOnFocusContext= */ false);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         verify(mAutocompleteController).start(url, pageClassification, "test", 4, false);
     }
@@ -493,8 +516,8 @@
         when(mTextStateProvider.getSelectionEnd()).thenReturn(4);
 
         mMediator.onNativeInitialized();
-        mMediator.onTextChanged("test");
-        mMediator.onTextChanged("nottest");
+        mMediator.onTextChanged("test", /* isOnFocusContext= */ false);
+        mMediator.onTextChanged("nottest", /* isOnFocusContext= */ false);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         verify(mAutocompleteController, times(1))
                 .start(url, pageClassification, "nottest", 4, false);
@@ -522,7 +545,8 @@
         mMediator.onOmniboxSessionStateChange(false);
         mMediator.onOmniboxSessionStateChange(true);
         ShadowLooper.runUiThreadTasks();
-        verify(mAutocompleteController, never()).startZeroSuggest(any(), any(), anyInt(), any());
+        verify(mAutocompleteController, never())
+                .startZeroSuggest(any(), any(), anyInt(), any(), /* isOnFocusContext= */ eq(false));
 
         // Simulate native being initialized. Make sure we only ever issue one request, even if
         // there are multiple requests to activate the autocomplete session.
@@ -530,7 +554,8 @@
         ShadowLooper.runUiThreadTasks();
         mMediator.onOmniboxSessionStateChange(true);
         verify(mAutocompleteController, times(1))
-                .startZeroSuggest("", url, pageClassification, title);
+                .startZeroSuggest(
+                        "", url, pageClassification, title, /* isOnFocusContext= */ false);
     }
 
     @Test
@@ -550,12 +575,14 @@
         mMediator.onOmniboxSessionStateChange(true);
         mMediator.onOmniboxSessionStateChange(false);
         ShadowLooper.runUiThreadTasks();
-        verify(mAutocompleteController, never()).startZeroSuggest(any(), any(), anyInt(), any());
+        verify(mAutocompleteController, never())
+                .startZeroSuggest(any(), any(), anyInt(), any(), /* isOnFocusContext= */ eq(false));
 
         // Simulate native being inititalized. Make sure no suggest requests are sent.
         mMediator.onNativeInitialized();
         ShadowLooper.runUiThreadTasks();
-        verify(mAutocompleteController, never()).startZeroSuggest(any(), any(), anyInt(), any());
+        verify(mAutocompleteController, never())
+                .startZeroSuggest(any(), any(), anyInt(), any(), /* isOnFocusContext= */ eq(false));
     }
 
     @Test
@@ -575,21 +602,23 @@
 
         // Simulate URL being focus changes, and that user typed text and deleted it.
         mMediator.onOmniboxSessionStateChange(true);
-        mMediator.onTextChanged("A");
-        mMediator.onTextChanged("");
-        mMediator.onTextChanged("A");
+        mMediator.onTextChanged("A", /* isOnFocusContext= */ false);
+        mMediator.onTextChanged("", /* isOnFocusContext= */ false);
+        mMediator.onTextChanged("A", /* isOnFocusContext= */ false);
 
         ShadowLooper.runUiThreadTasks();
         verify(mAutocompleteController, never())
                 .start(any(), anyInt(), any(), anyInt(), anyBoolean());
-        verify(mAutocompleteController, never()).startZeroSuggest(any(), any(), anyInt(), any());
+        verify(mAutocompleteController, never())
+                .startZeroSuggest(any(), any(), anyInt(), any(), /* isOnFocusContext= */ eq(false));
 
         mMediator.onNativeInitialized();
         ShadowLooper.runUiThreadTasks();
         verify(mAutocompleteController, times(1)).start(url, pageClassification, "A", 0, true);
         verify(mAutocompleteController, times(1))
                 .start(any(), anyInt(), any(), anyInt(), anyBoolean());
-        verify(mAutocompleteController, never()).startZeroSuggest(any(), any(), anyInt(), any());
+        verify(mAutocompleteController, never())
+                .startZeroSuggest(any(), any(), anyInt(), any(), /* isOnFocusContext= */ eq(false));
     }
 
     @Test
@@ -609,19 +638,21 @@
         when(mTextStateProvider.getTextWithAutocomplete()).thenReturn("");
 
         // Simulate URL being focus changes, and that user typed text and deleted it.
-        mMediator.onTextChanged("A");
-        mMediator.onTextChanged("");
+        mMediator.onTextChanged("A", /* isOnFocusContext= */ false);
+        mMediator.onTextChanged("", /* isOnFocusContext= */ false);
 
         ShadowLooper.runUiThreadTasks();
         verify(mAutocompleteController, never())
                 .start(any(), anyInt(), any(), anyInt(), anyBoolean());
-        verify(mAutocompleteController, never()).startZeroSuggest(any(), any(), anyInt(), any());
+        verify(mAutocompleteController, never())
+                .startZeroSuggest(any(), any(), anyInt(), any(), /* isOnFocusContext= */ eq(false));
 
         mMediator.onNativeInitialized();
         ShadowLooper.runUiThreadTasks();
         verify(mAutocompleteController, never())
                 .start(any(), anyInt(), any(), anyInt(), anyBoolean());
-        verify(mAutocompleteController, times(1)).startZeroSuggest(any(), any(), anyInt(), any());
+        verify(mAutocompleteController, times(1))
+                .startZeroSuggest(any(), any(), anyInt(), any(), /* isOnFocusContext= */ eq(false));
     }
 
     @Test
@@ -839,7 +870,12 @@
         mMediator.onOmniboxSessionStateChange(true);
         ShadowLooper.runUiThreadTasks();
         verify(mAutocompleteController)
-                .startZeroSuggest(url.getSpec(), url, pageClassification, title);
+                .startZeroSuggest(
+                        url.getSpec(),
+                        url,
+                        pageClassification,
+                        title,
+                        /* isOnFocusContext= */ false);
     }
 
     @Test
@@ -860,12 +896,15 @@
         // Signal focus prior to initializing native; confirm that zero suggest is not triggered.
         mMediator.onOmniboxSessionStateChange(true);
         ShadowLooper.runUiThreadTasks();
-        verify(mAutocompleteController, never()).startZeroSuggest(any(), any(), anyInt(), any());
+        verify(mAutocompleteController, never())
+                .startZeroSuggest(any(), any(), anyInt(), any(), /* isOnFocusContext= */ eq(false));
 
         // Initialize native and ensure zero suggest is triggered.
         mMediator.onNativeInitialized();
         ShadowLooper.runUiThreadTasks();
-        verify(mAutocompleteController).startZeroSuggest("", url, pageClassification, title);
+        verify(mAutocompleteController)
+                .startZeroSuggest(
+                        "", url, pageClassification, title, /* isOnFocusContext= */ false);
     }
 
     @Test
@@ -876,7 +915,7 @@
         mMediator.onNativeInitialized();
         mMediator.onOmniboxSessionStateChange(true);
         Assert.assertEquals(mMediator.getEditSessionStateForTest(), EditSessionState.INACTIVE);
-        mMediator.onTextChanged("n");
+        mMediator.onTextChanged("n", /* isOnFocusContext= */ false);
         Assert.assertEquals(
                 mMediator.getEditSessionStateForTest(), EditSessionState.ACTIVATED_BY_USER_INPUT);
 
@@ -1029,7 +1068,9 @@
         mMediator.onNativeInitialized();
 
         mMediator.onOmniboxSessionStateChange(true);
-        verify(mAutocompleteController).startZeroSuggest("", url, pageClassification, title);
+        verify(mAutocompleteController)
+                .startZeroSuggest(
+                        "", url, pageClassification, title, /* isOnFocusContext= */ false);
         verifySuggestionRequestToUiModelHistograms(0, null, 0, null);
 
         // Report first results. Observe first results histogram reported.
@@ -1064,7 +1105,9 @@
         mMediator.onNativeInitialized();
 
         mMediator.onOmniboxSessionStateChange(true);
-        verify(mAutocompleteController).startZeroSuggest("", url, pageClassification, title);
+        verify(mAutocompleteController)
+                .startZeroSuggest(
+                        "", url, pageClassification, title, /* isOnFocusContext= */ false);
         verifySuggestionRequestToUiModelHistograms(0, null, 0, null);
 
         // Report first results. Observe first results histogram reported.
@@ -1095,7 +1138,9 @@
         mMediator.onNativeInitialized();
 
         mMediator.onOmniboxSessionStateChange(true);
-        verify(mAutocompleteController).startZeroSuggest("", url, pageClassification, title);
+        verify(mAutocompleteController)
+                .startZeroSuggest(
+                        "", url, pageClassification, title, /* isOnFocusContext= */ false);
         verifySuggestionRequestToUiModelHistograms(0, null, 0, null);
 
         // Cancel the interaction.
@@ -1126,7 +1171,9 @@
         mMediator.onNativeInitialized();
 
         mMediator.onOmniboxSessionStateChange(true);
-        verify(mAutocompleteController).startZeroSuggest("", url, pageClassification, title);
+        verify(mAutocompleteController)
+                .startZeroSuggest(
+                        "", url, pageClassification, title, /* isOnFocusContext= */ false);
         verifySuggestionRequestToUiModelHistograms(0, null, 0, null);
 
         // Report first result as final. Observe both metrics reported.
@@ -1151,7 +1198,9 @@
         mMediator.onNativeInitialized();
 
         mMediator.onOmniboxSessionStateChange(true);
-        verify(mAutocompleteController).startZeroSuggest("", url, pageClassification, title);
+        verify(mAutocompleteController)
+                .startZeroSuggest(
+                        "", url, pageClassification, title, /* isOnFocusContext= */ false);
         verifySuggestionRequestToUiModelHistograms(0, null, 0, null);
 
         // Report first result as final. Observe both metrics reported.
@@ -1161,7 +1210,7 @@
 
         // No change on key press. No unexpected recordings.
         // Need to run looper here to flush the pending operation.
-        mMediator.onTextChanged("a");
+        mMediator.onTextChanged("a", /* isOnFocusContext= */ false);
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         verifySuggestionRequestToUiModelHistograms(1, 150, 0, null);
 
@@ -1374,7 +1423,7 @@
         for (var pageClass : PageClassification.values()) {
             setUpLocationBarDataProvider(PAGE_URL, PAGE_TITLE, pageClass.getNumber());
 
-            mMediator.onTextChanged("");
+            mMediator.onTextChanged("", /* isOnFocusContext= */ false);
 
             // Should only be invoked if page class is eligible.
             int numTimesInvoked = eligibleClasses.contains(pageClass.getNumber()) ? 1 : 0;
@@ -1392,7 +1441,7 @@
         for (var pageClass : PageClassification.values()) {
             setUpLocationBarDataProvider(PAGE_URL, PAGE_TITLE, pageClass.getNumber());
 
-            mMediator.onTextChanged("text");
+            mMediator.onTextChanged("text", /* isOnFocusContext= */ false);
 
             // Should only be invoked if page class is eligible.
             verify(ShadowCachedSuggestionsManager.mock, never()).readFromCache();
@@ -1410,7 +1459,7 @@
         for (var pageClass : PageClassification.values()) {
             setUpLocationBarDataProvider(PAGE_URL, PAGE_TITLE, pageClass.getNumber());
 
-            mMediator.onTextChanged("");
+            mMediator.onTextChanged("", /* isOnFocusContext= */ false);
 
             // Should only be invoked if page class is eligible.
             verify(ShadowCachedSuggestionsManager.mock, never()).readFromCache();
@@ -1431,7 +1480,7 @@
         for (var pageClass : PageClassification.values()) {
             setUpLocationBarDataProvider(PAGE_URL, PAGE_TITLE, pageClass.getNumber());
 
-            mMediator.onTextChanged("");
+            mMediator.onTextChanged("", /* isOnFocusContext= */ false);
 
             // Should only be invoked if page class is eligible.
             int numTimesInvoked = eligibleClasses.contains(pageClass.getNumber()) ? 1 : 0;
@@ -1448,7 +1497,7 @@
 
         for (var pageClass : PageClassification.values()) {
             setUpLocationBarDataProvider(PAGE_URL, PAGE_TITLE, pageClass.getNumber());
-            mMediator.onTextChanged("x");
+            mMediator.onTextChanged("x", /* isOnFocusContext= */ false);
             verify(ShadowCachedSuggestionsManager.mock, never()).saveToCache(any());
             clearInvocations(ShadowCachedSuggestionsManager.mock);
         }
@@ -1461,7 +1510,7 @@
 
         for (var pageClass : PageClassification.values()) {
             setUpLocationBarDataProvider(PAGE_URL, PAGE_TITLE, pageClass.getNumber());
-            mMediator.onTextChanged("");
+            mMediator.onTextChanged("", /* isOnFocusContext= */ false);
             verify(ShadowCachedSuggestionsManager.mock, never()).saveToCache(any());
             clearInvocations(ShadowCachedSuggestionsManager.mock);
         }
@@ -1546,11 +1595,12 @@
         when(mTextStateProvider.getTextWithAutocomplete()).thenReturn("");
 
         mMediator.onNativeInitialized();
-        mMediator.onTextChanged("");
+        mMediator.onTextChanged("", /* isOnFocusContext= */ false);
         verify(mAutocompleteController, never())
-                .startZeroSuggest("", url, pageClassification, title);
+                .startZeroSuggest(
+                        "", url, pageClassification, title, /* isOnFocusContext= */ false);
 
-        mMediator.onTextChanged("t");
+        mMediator.onTextChanged("t", /* isOnFocusContext= */ false);
         verify(mAutocompleteController, never())
                 .start(any(), anyInt(), any(), anyInt(), anyBoolean());
     }
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 60f4c69..c4ab738 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -621,10 +621,6 @@
       <message translateable="false" name="IDS_AUTOFILL_OPTIONS_HINT_3P_SETTING_READY" desc="Explanation that third-party autofill in Chrome will defer to the 3P password manager as selected in Android settings." formatter_data="android_java">
         Ready to use the autofill service that is configured in <ph name="BEGIN_LINK">&lt;link&gt;</ph>Android System Settings<ph name="END_LINK">&lt;/link&gt;</ph>.
       </message>
-      <!-- TODO(crbug.com/40898980): Use finalized string.-->
-      <message translateable="false" name="IDS_AUTOFILL_OPTIONS_HINT_3P_IN_INCOGNITO" desc="Explanation that a 3P password manager may prompt to save passwords in incognito." formatter_data="android_java">
-        Note: When using “other providers”, the browser informs the configured AutofillService about sites and fields visited in incognito.\nThe AutofillService uses this information to autofill your data and may store data in the process. Autofill data saved in incognito mode is stored even after incognito mode ends and available outside incognito mode.
-      </message>
       <message name="IDS_AUTOFILL_DELETE_SAVED_CVCS_CONFIRMATION_DIALOG_TITLE" desc="Title for the dialog where the user confirms their intention to delete their saved CVCs.">
         Delete saved security codes?
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_AUTOFILL_OPTIONS_HINT_3P_IN_INCOGNITO.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_AUTOFILL_OPTIONS_HINT_3P_IN_INCOGNITO.png.sha1
deleted file mode 100644
index 42bfa2d..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_AUTOFILL_OPTIONS_HINT_3P_IN_INCOGNITO.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f826edea0120b52f0ebadb55c2e5dabbb0b7d95e
\ No newline at end of file
diff --git a/chrome/browser/ui/ash/BUILD.gn b/chrome/browser/ui/ash/BUILD.gn
index bf35161..cbd026d 100644
--- a/chrome/browser/ui/ash/BUILD.gn
+++ b/chrome/browser/ui/ash/BUILD.gn
@@ -75,8 +75,6 @@
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/signin",
-    "//chrome/browser/ash/login/ui",
-    "//chrome/browser/ash/login/ui/login_screen_extension_ui",
     "//chrome/browser/ash/login/users/avatar",
     "//chrome/browser/ash/magic_boost",
     "//chrome/browser/ash/mahi",
@@ -136,6 +134,7 @@
     "//chrome/browser/ui/ash/input_method",
     "//chrome/browser/ui/ash/keyboard",
     "//chrome/browser/ui/ash/login",
+    "//chrome/browser/ui/ash/login/login_screen_extension_ui",
     "//chrome/browser/ui/ash/main_extra_parts",
     "//chrome/browser/ui/ash/media_client",
     "//chrome/browser/ui/ash/multi_user",
@@ -224,7 +223,6 @@
 
   allow_circular_includes_from = [
     "//chrome/browser/apps/app_service",
-    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ash",
     "//chrome/browser/ash/accessibility",
     "//chrome/browser/ash/app_list",
@@ -241,8 +239,6 @@
     "//chrome/browser/ash/login/lock",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
-    "//chrome/browser/ash/login/ui/login_screen_extension_ui",
     "//chrome/browser/ash/login/users/avatar",
     "//chrome/browser/ash/notifications",
     "//chrome/browser/ash/policy/dlp",
@@ -259,6 +255,8 @@
     "//chrome/browser/ui/ash/download_status",
     "//chrome/browser/ui/ash/game_dashboard",
     "//chrome/browser/ui/ash/holding_space",
+    "//chrome/browser/ui/ash/login",
+    "//chrome/browser/ui/ash/login/login_screen_extension_ui",
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/ash/network",
     "//chrome/browser/ui/ash/sharesheet",
@@ -346,6 +344,8 @@
     "//chrome/browser/ui/ash/in_session_auth:unit_tests",
     "//chrome/browser/ui/ash/input_method:unit_tests",
     "//chrome/browser/ui/ash/keyboard:unit_tests",
+    "//chrome/browser/ui/ash/login:unit_tests",
+    "//chrome/browser/ui/ash/login/login_screen_extension_ui:unit_tests",
     "//chrome/browser/ui/ash/media_client:unit_tests",
     "//chrome/browser/ui/ash/multi_user:unit_tests",
     "//chrome/browser/ui/ash/network:unit_tests",
@@ -380,7 +380,6 @@
     "//chrome/browser/ash/login/lock:test_support",
     "//chrome/browser/ash/login/session:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/profiles",
@@ -393,6 +392,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
     "//chrome/browser/ui/ash:test_support",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/new_window",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/browser/web_applications",
@@ -438,6 +438,8 @@
     "//chrome/browser/ui/ash/google_one:browser_tests",
     "//chrome/browser/ui/ash/holding_space:browser_tests",
     "//chrome/browser/ui/ash/keyboard:browser_tests",
+    "//chrome/browser/ui/ash/login:browser_tests",
+    "//chrome/browser/ui/ash/metrics:browser_tests",
     "//chrome/browser/ui/ash/network:browser_tests",
     "//chrome/browser/ui/ash/new_window:browser_tests",
     "//chrome/browser/ui/ash/picker:browser_tests",
diff --git a/chrome/browser/ui/ash/clipboard/BUILD.gn b/chrome/browser/ui/ash/clipboard/BUILD.gn
index cb9861e..2ade043 100644
--- a/chrome/browser/ui/ash/clipboard/BUILD.gn
+++ b/chrome/browser/ui/ash/clipboard/BUILD.gn
@@ -107,10 +107,10 @@
     "//chrome/browser",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//chromeos/ash/components/browser_context_helper",
diff --git a/chrome/browser/ui/ash/clipboard/DEPS b/chrome/browser/ui/ash/clipboard/DEPS
index 56250ebf..c8c9a7f 100644
--- a/chrome/browser/ui/ash/clipboard/DEPS
+++ b/chrome/browser/ui/ash/clipboard/DEPS
@@ -13,12 +13,12 @@
   "+chrome/app/chrome_command_ids.h",
   "+chrome/browser/ash/login/login_manager_test.h",
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/history",
   "+chrome/browser/profiles",
   "+chrome/browser/renderer_context_menu",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/browser.h",
   "+chrome/grit",
   "+chrome/test",
diff --git a/chrome/browser/ui/ash/clipboard/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard/clipboard_history_browsertest.cc
index 09376f9..91d340a 100644
--- a/chrome/browser/ui/ash/clipboard/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard/clipboard_history_browsertest.cc
@@ -37,13 +37,13 @@
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/history_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
 #include "chrome/browser/ui/ash/clipboard/clipboard_history_test_util.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/in_process_browser_test.h"
diff --git a/chrome/browser/ui/ash/desks/BUILD.gn b/chrome/browser/ui/ash/desks/BUILD.gn
index 9bbf379..59058012a 100644
--- a/chrome/browser/ui/ash/desks/BUILD.gn
+++ b/chrome/browser/ui/ash/desks/BUILD.gn
@@ -118,7 +118,6 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/system_web_apps",
@@ -127,6 +126,7 @@
     "//chrome/browser/policy:test_support",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/ash",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/browser/web_applications:web_applications_test_support",
diff --git a/chrome/browser/ui/ash/desks/DEPS b/chrome/browser/ui/ash/desks/DEPS
index 7cf3847..132c6c9 100644
--- a/chrome/browser/ui/ash/desks/DEPS
+++ b/chrome/browser/ui/ash/desks/DEPS
@@ -20,7 +20,6 @@
   "+chrome/browser/ash/floating_workspace",
   "+chrome/browser/ash/login/login_manager_test.h",
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/ash/system_web_apps/apps",
@@ -32,6 +31,7 @@
   "+chrome/browser/prefs",
   "+chrome/browser/profiles",
   "+chrome/browser/sync",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/ash/shelf",
   "+chrome/browser/ui/ash/system_web_apps",
diff --git a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
index 2eb106b..81a74377 100644
--- a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
@@ -72,7 +72,6 @@
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/system_web_apps/apps/os_url_handler_system_web_app_info.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
@@ -84,6 +83,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/desks/chrome_desks_util.h"
 #include "chrome/browser/ui/ash/desks/desks_templates_app_launch_handler.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/browser.h"
@@ -3195,8 +3195,7 @@
   // Add our browser under test, this is the only way to launch an app
   // via the BrowserManager.
   crosapi::BrowserManager::Get()->CreateBrowserWithRestoredData(
-      {GURL(kExampleUrl1)}, {0, 0, 256, 256}, {},
-      ui::WindowShowState::SHOW_STATE_DEFAULT,
+      {GURL(kExampleUrl1)}, {0, 0, 256, 256}, {}, ui::SHOW_STATE_DEFAULT,
       /*active_tab_index=*/0, /*first_non_pinned_tab_index=*/0, kTestAppName,
       kTestWindowId, /*lacros_profile_id=*/0);
   LacrosWindowWaiter waiter;
diff --git a/chrome/browser/ui/ash/login/BUILD.gn b/chrome/browser/ui/ash/login/BUILD.gn
index 320e8cc..c583aea 100644
--- a/chrome/browser/ui/ash/login/BUILD.gn
+++ b/chrome/browser/ui/ash/login/BUILD.gn
@@ -8,37 +8,185 @@
 
 static_library("login") {
   sources = [
+    "captive_portal_view.cc",
+    "captive_portal_view.h",
+    "captive_portal_window_proxy.cc",
+    "captive_portal_window_proxy.h",
+    "input_events_blocker.cc",
+    "input_events_blocker.h",
+    "kiosk_app_menu_controller.cc",
+    "kiosk_app_menu_controller.h",
+    "login_display_host.cc",
+    "login_display_host.h",
+    "login_display_host_common.cc",
+    "login_display_host_common.h",
+    "login_display_host_mojo.cc",
+    "login_display_host_mojo.h",
+    "login_display_host_webui.cc",
+    "login_display_host_webui.h",
+    "login_feedback.cc",
+    "login_feedback.h",
     "login_screen_client_impl.cc",
     "login_screen_client_impl.h",
+    "login_ui_pref_controller.cc",
+    "login_ui_pref_controller.h",
+    "login_web_dialog.cc",
+    "login_web_dialog.h",
+    "oobe_dialog_size_utils.cc",
+    "oobe_dialog_size_utils.h",
+    "oobe_dialog_util_impl.cc",
+    "oobe_dialog_util_impl.h",
+    "oobe_ui_dialog_delegate.cc",
+    "oobe_ui_dialog_delegate.h",
+    "signin_ui.h",
+    "simple_web_view_dialog.cc",
+    "simple_web_view_dialog.h",
+    "user_adding_screen.cc",
+    "user_adding_screen.h",
+    "user_adding_screen_input_methods_controller.cc",
+    "user_adding_screen_input_methods_controller.h",
+    "webui_login_view.cc",
+    "webui_login_view.h",
   ]
 
   public_deps = [
     "//ash",
     "//ash/public/cpp",
     "//base",
+    "//chrome/browser:browser_public_dependencies",
+    "//chrome/browser:primitives",
+    "//chrome/browser/ash/app_mode",
+    "//chrome/browser/ash/customization",
+    "//chrome/browser/ash/login/oobe_quick_start",
+    "//chrome/browser/ash/profiles",
+    "//chrome/browser/ash/tpm",
+    "//chrome/browser/ui:browser_list",
+    "//chrome/browser/ui/ash/keyboard",
+    "//chromeos/ash/components/audio",
+    "//chromeos/ash/components/dbus/session_manager",
+    "//chromeos/ash/components/login/auth",
+    "//chromeos/ash/components/login/auth/public:authpublic",
+    "//chromeos/ash/components/login/auth/public:challenge_response_key",
+    "//components/account_id",
+    "//components/keep_alive_registry",
+    "//components/login",
+    "//components/prefs",
+    "//components/session_manager/core",
     "//components/user_manager",
+    "//components/user_manager:common",
+    "//components/web_modal",
+    "//content/public/browser",
+    "//ui/base",
+    "//ui/base/ime/ash",
+    "//ui/display",
+    "//ui/events",
+    "//ui/events/devices",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/views",
+    "//ui/views/controls/webview",
+    "//ui/web_dialogs",
+    "//url",
   ]
 
   deps = [
+    "//ash/constants",
     "//ash/webui/settings/public/constants:mojom",
+    "//base:i18n",
+    "//chrome/app:command_ids",
+    "//chrome/app:generated_resources",
+    "//chrome/app/theme:theme_resources",
+    "//chrome/browser:browser_process",
     "//chrome/browser:browser_public_dependencies",
+    "//chrome/browser:resources",
+    "//chrome/browser/ash/accessibility",
+    "//chrome/browser/ash/app_mode",
+    "//chrome/browser/ash/app_mode/web_app",
+    "//chrome/browser/ash/arc",
+    "//chrome/browser/ash/arc/optin",
+    "//chrome/browser/ash/attestation",
+    "//chrome/browser/ash/base",
+    "//chrome/browser/ash/boot_times_recorder",
     "//chrome/browser/ash/child_accounts/parent_access_code",
     "//chrome/browser/ash/input_method",
     "//chrome/browser/ash/login",
-    "//chrome/browser/ash/login/lock",
-    "//chrome/browser/ash/login/ui",
+    "//chrome/browser/ash/login/app_mode",
+    "//chrome/browser/ash/login/quick_unlock",
+    "//chrome/browser/ash/login/session",
+    "//chrome/browser/ash/nearby",
+    "//chrome/browser/ash/net",
+    "//chrome/browser/ash/policy/core",
+    "//chrome/browser/ash/policy/enrollment",
+    "//chrome/browser/ash/policy/handlers",
+    "//chrome/browser/ash/policy/remote_commands/crd",
+    "//chrome/browser/ash/system",
+    "//chrome/browser/media/webrtc",
+    "//chrome/browser/safe_browsing",
+    "//chrome/browser/themes",
+    "//chrome/browser/ui/autofill",
+    "//chrome/browser/ui/content_settings",
+    "//chrome/browser/ui/views/toolbar",
+    "//chrome/browser/ui/webui/ash/diagnostics_dialog",
+    "//chrome/browser/ui/webui/ash/internet",
     "//chrome/common",
+    "//chrome/common:constants",
+    "//chrome/common:non_code_constants",
+    "//chromeos/ash/components/attestation",
+    "//chromeos/ash/components/browser_context_helper",
+    "//chromeos/ash/components/dbus/userdataauth",
+    "//chromeos/ash/components/geolocation",
     "//chromeos/ash/components/install_attributes",
+    "//chromeos/ash/components/language_preferences",
+    "//chromeos/ash/components/login/login_state",
+    "//chromeos/ash/components/network",
+    "//chromeos/ash/components/osauth/public",
     "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/settings",
+    "//chromeos/ash/components/timezone",
+    "//components/captive_portal/core",
+    "//components/constrained_window",
+    "//components/content_settings/core/common",
+    "//components/language/core/browser",
+    "//components/language/core/common",
+    "//components/omnibox/browser:location_bar",
+    "//components/password_manager/core/browser",
+    "//components/session_manager:base",
     "//components/session_manager/core",
+    "//components/startup_metric_utils",
+    "//components/strings:components_strings",
+    "//content/public/common",
+    "//extensions:extensions_browser_resources",
+    "//extensions/browser",
+    "//extensions/browser/api/feedback_private",
+    "//extensions/common",
+    "//extensions/common:mojom",
+    "//google_apis",
+    "//ipc:message_support",
+    "//services/audio/public/cpp",
+    "//third_party/blink/public/common:headers",
+    "//ui/accessibility:ax_base",
+    "//ui/aura",
+    "//ui/base:features",
     "//ui/base/ime/ash",
+    "//ui/color:color_headers",
+    "//ui/compositor",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
   ]
 
   allow_circular_includes_from = [
+    "//chrome/browser/ash/app_mode",
+    "//chrome/browser/ash/arc",
+    "//chrome/browser/ash/arc/optin",
     "//chrome/browser/ash/input_method",
     "//chrome/browser/ash/login",
-    "//chrome/browser/ash/login/lock",
-    "//chrome/browser/ash/login/ui",
+    "//chrome/browser/ash/login/app_mode",
+    "//chrome/browser/ash/login/session",
+    "//chrome/browser/ash/net",
+    "//chrome/browser/ash/policy/enrollment",
+    "//chrome/browser/ash/policy/handlers",
+    "//chrome/browser/ash/policy/remote_commands/crd",
+    "//chrome/browser/ash/system",
   ]
 }
 
@@ -46,11 +194,102 @@
   testonly = true
 
   sources = [
+    "fake_login_display_host.cc",
+    "fake_login_display_host.h",
+    "mock_login_display_host.cc",
+    "mock_login_display_host.h",
+    "mock_signin_ui.cc",
+    "mock_signin_ui.h",
     "test_login_screen.cc",
     "test_login_screen.h",
     "test_login_screen_model.cc",
     "test_login_screen_model.h",
   ]
 
-  public_deps = [ "//ash/public/cpp" ]
+  public_deps = [
+    ":login",
+    "//ash/public/cpp",
+    "//base",
+    "//chrome/browser/ash/app_mode",
+    "//chrome/browser/ash/login",
+    "//chromeos/ash/components/login/auth/public:authpublic",
+    "//components/login",
+    "//components/prefs",
+    "//components/user_manager:common",
+    "//testing/gmock",
+  ]
+
+  deps = [
+    "//chrome/browser/ui",
+    "//chrome/browser/ui/webui/ash/login",
+    "//components/session_manager/core",
+  ]
+}
+
+source_set("browser_tests") {
+  testonly = true
+
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+  sources = [
+    "captive_portal_window_browsertest.cc",
+    "login_feedback_browsertest.cc",
+    "login_ui_pref_controller_browsertest.cc",
+    "login_web_dialog_browsertest.cc",
+    "simple_web_view_dialog_browsertest.cc",
+    "user_adding_screen_browsertest.cc",
+  ]
+
+  deps = [
+    ":login",
+    "//ash/constants",
+    "//ash/public/cpp",
+    "//ash/webui/os_feedback_ui:url_constants",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser:browser_process",
+    "//chrome/browser/ash/login:test_support",
+    "//chrome/browser/ash/login/lock",
+    "//chrome/browser/ash/login/lock:test_support",
+    "//chrome/browser/ash/login/screens",
+    "//chrome/browser/ash/net",
+    "//chrome/browser/ash/policy/core:test_support",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui",
+    "//chrome/browser/ui/webui/ash/os_feedback_dialog",
+    "//chrome/browser/ui/webui/ash/system_web_dialog",
+    "//chrome/common:constants",
+    "//chrome/test:test_support",
+    "//chrome/test:test_support_ui",
+    "//chromeos/ash/components/dbus/shill",
+    "//chromeos/ash/components/network:test_support",
+    "//components/policy:generated",
+    "//components/policy:policy_code_generate",
+    "//components/policy/proto",
+    "//components/prefs",
+    "//components/prefs:test_support",
+    "//components/session_manager/core",
+    "//components/user_manager",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//net:test_support",
+    "//testing/gtest",
+    "//ui/aura",
+    "//ui/events:test_support",
+    "//ui/views",
+    "//ui/views:test_support",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [ "oobe_dialog_size_utils_unittest.cc" ]
+
+  deps = [
+    ":login",
+    "//base/test:test_support",
+    "//testing/gtest",
+    "//ui/gfx/geometry",
+  ]
 }
diff --git a/chrome/browser/ui/ash/login/DEPS b/chrome/browser/ui/ash/login/DEPS
index c560567..cb25c88 100644
--- a/chrome/browser/ui/ash/login/DEPS
+++ b/chrome/browser/ui/ash/login/DEPS
@@ -10,12 +10,75 @@
   # Existing dependencies within //chrome. There is an active effort to
   # refactor ash codes in //chrome to break these dependencies; see b/332804822.
   # Whenever possible, avoid adding new //chrome dependencies to this list.
+  "+chrome/app",
+  "+chrome/browser/ash/accessibility",
+  "+chrome/browser/ash/app_mode",
+  "+chrome/browser/ash/attestation",
+  "+chrome/browser/ash/auth",
+  "+chrome/browser/ash/base",
+  "+chrome/browser/ash/boot_times_recorder",
   "+chrome/browser/ash/child_accounts/parent_access_code",
+  "+chrome/browser/ash/customization",
+  "+chrome/browser/ash/extensions/login_screen_ui",
+  "+chrome/browser/ash/first_run",
   "+chrome/browser/ash/login",
+  "+chrome/browser/ash/nearby",
+  "+chrome/browser/ash/net",
+  "+chrome/browser/ash/policy/core",
+  "+chrome/browser/ash/policy/enrollment",
+  "+chrome/browser/ash/profiles",
+  "+chrome/browser/ash/settings",
+  "+chrome/browser/ash/system",
+  "+chrome/browser/ash/tpm",
+  "+chrome/browser/browser_process.h",
+  "+chrome/browser/browser_process_platform_part.h",
+  "+chrome/browser/certificate_provider",
+  "+chrome/browser/command_updater_delegate.h",
+  "+chrome/browser/command_updater_impl.h",
+  "+chrome/browser/lifetime",
+  "+chrome/browser/media/webrtc",
+  "+chrome/browser/password_manager",
   "+chrome/browser/profiles",
+  "+chrome/browser/renderer_preferences_util.h",
+  "+chrome/browser/safe_browsing",
+  "+chrome/browser/sessions",
+  "+chrome/browser/signin",
+  "+chrome/browser/ssl",
+  "+chrome/browser/themes",
+  "+chrome/browser/ui/ash/keyboard",
+  "+chrome/browser/ui/ash/system",
   "+chrome/browser/ui/ash/wallpaper",
+  "+chrome/browser/ui/autofill",
+  "+chrome/browser/ui/browser.h",
+  "+chrome/browser/ui/browser_dialogs.h",
+  "+chrome/browser/ui/browser_finder.h",
+  "+chrome/browser/ui/browser_list.h",
+  "+chrome/browser/ui/browser_list_observer.h",
+  "+chrome/browser/ui/browser_window.h",
+  "+chrome/browser/ui/chrome_pages.h",
+  "+chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h",
+  "+chrome/browser/ui/content_settings",
+  "+chrome/browser/ui/login",
   "+chrome/browser/ui/settings_window_manager_chromeos.h",
+  "+chrome/browser/ui/toolbar",
+  "+chrome/browser/ui/view_ids.h",
+  "+chrome/browser/ui/views/location_bar/location_bar_view.h",
+  "+chrome/browser/ui/views/toolbar/reload_button.h",
+  "+chrome/browser/ui/webui/ash/diagnostics_dialog",
+  "+chrome/browser/ui/webui/ash/internet",
   "+chrome/browser/ui/webui/ash/lock_screen_reauth",
   "+chrome/browser/ui/webui/ash/login",
+  "+chrome/browser/ui/webui/ash/login",
+  "+chrome/browser/ui/webui/ash/os_feedback_dialog",
+  "+chrome/browser/ui/webui/ash/system_web_dialog/system_web_dialog_delegate.h",
+  "+chrome/browser/ui/webui/chrome_web_contents_handler.h",
+  "+chrome/browser/ui/webui/feedback",
   "+chrome/common",
+  "+chrome/common/channel_info.h",
+  "+chrome/common/chrome_constants.h",
+  "+chrome/common/chrome_switches.h",
+  "+chrome/common/pref_names.h",
+  "+chrome/grit",
+  "+chrome/test/base",
+  "+chrome/test/views",
 ]
diff --git a/chrome/browser/ui/ash/login/OWNERS b/chrome/browser/ui/ash/login/OWNERS
index 451671b..8793caf3 100644
--- a/chrome/browser/ui/ash/login/OWNERS
+++ b/chrome/browser/ui/ash/login/OWNERS
@@ -1 +1,5 @@
 file://ash/login/OWNERS
+
+per-file kiosk_app*=file://chromeos/components/kiosk/OWNERS
+per-file login_display_host*.*=set noparent
+per-file login_display_host*.*=file://ash/login/LOGIN_LOCK_OWNERS
diff --git a/chrome/browser/ash/login/ui/captive_portal_view.cc b/chrome/browser/ui/ash/login/captive_portal_view.cc
similarity index 95%
rename from chrome/browser/ash/login/ui/captive_portal_view.cc
rename to chrome/browser/ui/ash/login/captive_portal_view.cc
index 96ab8ac..56866db 100644
--- a/chrome/browser/ash/login/ui/captive_portal_view.cc
+++ b/chrome/browser/ui/ash/login/captive_portal_view.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/captive_portal_view.h"
+#include "chrome/browser/ui/ash/login/captive_portal_view.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ash/login/ui/captive_portal_window_proxy.h"
+#include "chrome/browser/ui/ash/login/captive_portal_window_proxy.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state.h"
diff --git a/chrome/browser/ash/login/ui/captive_portal_view.h b/chrome/browser/ui/ash/login/captive_portal_view.h
similarity index 85%
rename from chrome/browser/ash/login/ui/captive_portal_view.h
rename to chrome/browser/ui/ash/login/captive_portal_view.h
index 20040e5..1b818b5 100644
--- a/chrome/browser/ash/login/ui/captive_portal_view.h
+++ b/chrome/browser/ui/ash/login/captive_portal_view.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ASH_LOGIN_UI_CAPTIVE_PORTAL_VIEW_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_CAPTIVE_PORTAL_VIEW_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_CAPTIVE_PORTAL_VIEW_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_CAPTIVE_PORTAL_VIEW_H_
 
 #include "base/memory/raw_ptr.h"
-#include "chrome/browser/ash/login/ui/simple_web_view_dialog.h"
+#include "chrome/browser/ui/ash/login/simple_web_view_dialog.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 
 namespace ash {
@@ -49,4 +49,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_CAPTIVE_PORTAL_VIEW_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_CAPTIVE_PORTAL_VIEW_H_
diff --git a/chrome/browser/ash/login/ui/captive_portal_window_browsertest.cc b/chrome/browser/ui/ash/login/captive_portal_window_browsertest.cc
similarity index 98%
rename from chrome/browser/ash/login/ui/captive_portal_window_browsertest.cc
rename to chrome/browser/ui/ash/login/captive_portal_window_browsertest.cc
index ee30163..484ac672 100644
--- a/chrome/browser/ash/login/ui/captive_portal_window_browsertest.cc
+++ b/chrome/browser/ui/ash/login/captive_portal_window_browsertest.cc
@@ -13,10 +13,10 @@
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/captive_portal_window_proxy.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/net/network_portal_detector_test_impl.h"
+#include "chrome/browser/ui/ash/login/captive_portal_window_proxy.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
diff --git a/chrome/browser/ash/login/ui/captive_portal_window_proxy.cc b/chrome/browser/ui/ash/login/captive_portal_window_proxy.cc
similarity index 95%
rename from chrome/browser/ash/login/ui/captive_portal_window_proxy.cc
rename to chrome/browser/ui/ash/login/captive_portal_window_proxy.cc
index fdcc9c4f..be8a526 100644
--- a/chrome/browser/ash/login/ui/captive_portal_window_proxy.cc
+++ b/chrome/browser/ui/ash/login/captive_portal_window_proxy.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/captive_portal_window_proxy.h"
+#include "chrome/browser/ui/ash/login/captive_portal_window_proxy.h"
 
 #include "base/memory/raw_ptr.h"
 #include "base/task/single_thread_task_runner.h"
-#include "chrome/browser/ash/login/ui/captive_portal_view.h"
 #include "chrome/browser/themes/custom_theme_supplier.h"
 #include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/ui/ash/login/captive_portal_view.h"
 #include "chrome/browser/ui/webui/ash/internet/internet_detail_dialog.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
@@ -135,8 +135,9 @@
 }
 
 void CaptivePortalWindowProxy::Close() {
-  if (GetState() == STATE_DISPLAYED)
+  if (GetState() == STATE_DISPLAYED) {
     widget_->Close();
+  }
   captive_portal_view_.reset();
 }
 
@@ -167,8 +168,9 @@
 
   DCHECK_EQ(STATE_IDLE, GetState());
 
-  for (auto& observer : observers_)
+  for (auto& observer : observers_) {
     observer.OnAfterCaptivePortalHidden();
+  }
 }
 
 void CaptivePortalWindowProxy::InitCaptivePortalView(
@@ -184,15 +186,17 @@
 }
 
 CaptivePortalWindowProxy::State CaptivePortalWindowProxy::GetState() const {
-  if (!widget_)
+  if (!widget_) {
     return captive_portal_view_ ? STATE_WAITING_FOR_REDIRECTION : STATE_IDLE;
+  }
   DCHECK(!captive_portal_view_);
   return STATE_DISPLAYED;
 }
 
 void CaptivePortalWindowProxy::DetachFromWidget(views::Widget* widget) {
-  if (!widget_ || widget_ != widget)
+  if (!widget_ || widget_ != widget) {
     return;
+  }
   widget_->RemoveObserver(this);
   widget_ = nullptr;
 }
diff --git a/chrome/browser/ash/login/ui/captive_portal_window_proxy.h b/chrome/browser/ui/ash/login/captive_portal_window_proxy.h
similarity index 95%
rename from chrome/browser/ash/login/ui/captive_portal_window_proxy.h
rename to chrome/browser/ui/ash/login/captive_portal_window_proxy.h
index 9be24d58..ff33a73cd 100644
--- a/chrome/browser/ash/login/ui/captive_portal_window_proxy.h
+++ b/chrome/browser/ui/ash/login/captive_portal_window_proxy.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 CHROME_BROWSER_ASH_LOGIN_UI_CAPTIVE_PORTAL_WINDOW_PROXY_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_CAPTIVE_PORTAL_WINDOW_PROXY_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_CAPTIVE_PORTAL_WINDOW_PROXY_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_CAPTIVE_PORTAL_WINDOW_PROXY_H_
 
 #include <memory>
 
@@ -119,4 +119,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_CAPTIVE_PORTAL_WINDOW_PROXY_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_CAPTIVE_PORTAL_WINDOW_PROXY_H_
diff --git a/chrome/browser/ash/login/ui/fake_login_display_host.cc b/chrome/browser/ui/ash/login/fake_login_display_host.cc
similarity index 97%
rename from chrome/browser/ash/login/ui/fake_login_display_host.cc
rename to chrome/browser/ui/ash/login/fake_login_display_host.cc
index 0759b78..fe6e4308 100644
--- a/chrome/browser/ash/login/ui/fake_login_display_host.cc
+++ b/chrome/browser/ui/ash/login/fake_login_display_host.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 
 #include "base/notreached.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
@@ -32,8 +32,9 @@
       oobe_metrics_helper_(std::make_unique<OobeMetricsHelper>()) {
   // Only one SessionManager can be instantiated at a time. Check to see if one
   // has already been instantiated before creating one.
-  if (!session_manager::SessionManager::Get())
+  if (!session_manager::SessionManager::Get()) {
     session_manager_ = std::make_unique<session_manager::SessionManager>();
+  }
 }
 
 FakeLoginDisplayHost::~FakeLoginDisplayHost() = default;
diff --git a/chrome/browser/ash/login/ui/fake_login_display_host.h b/chrome/browser/ui/ash/login/fake_login_display_host.h
similarity index 93%
rename from chrome/browser/ash/login/ui/fake_login_display_host.h
rename to chrome/browser/ui/ash/login/fake_login_display_host.h
index b04c3be..6a072f2 100644
--- a/chrome/browser/ash/login/ui/fake_login_display_host.h
+++ b/chrome/browser/ui/ash/login/fake_login_display_host.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ASH_LOGIN_UI_FAKE_LOGIN_DISPLAY_HOST_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_FAKE_LOGIN_DISPLAY_HOST_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_FAKE_LOGIN_DISPLAY_HOST_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_FAKE_LOGIN_DISPLAY_HOST_H_
 
 #include <memory>
 #include <optional>
 #include <string>
 
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "components/user_manager/user_type.h"
 
 namespace session_manager {
@@ -97,4 +97,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_FAKE_LOGIN_DISPLAY_HOST_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_FAKE_LOGIN_DISPLAY_HOST_H_
diff --git a/chrome/browser/ash/login/ui/input_events_blocker.cc b/chrome/browser/ui/ash/login/input_events_blocker.cc
similarity index 90%
rename from chrome/browser/ash/login/ui/input_events_blocker.cc
rename to chrome/browser/ui/ash/login/input_events_blocker.cc
index 6c0b37f..e1854e5b 100644
--- a/chrome/browser/ash/login/ui/input_events_blocker.cc
+++ b/chrome/browser/ui/ash/login/input_events_blocker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/input_events_blocker.h"
+#include "chrome/browser/ui/ash/login/input_events_blocker.h"
 
 #include "ash/shell.h"
 #include "base/logging.h"
@@ -16,8 +16,9 @@
 }
 
 InputEventsBlocker::~InputEventsBlocker() {
-  if (Shell::HasInstance())
+  if (Shell::HasInstance()) {
     Shell::Get()->RemovePreTargetHandler(this);
+  }
   VLOG(1) << "InputEventsBlocker " << this << " destroyed.";
 }
 
diff --git a/chrome/browser/ash/login/ui/input_events_blocker.h b/chrome/browser/ui/ash/login/input_events_blocker.h
similarity index 81%
rename from chrome/browser/ash/login/ui/input_events_blocker.h
rename to chrome/browser/ui/ash/login/input_events_blocker.h
index 4461417..d0d7071f 100644
--- a/chrome/browser/ash/login/ui/input_events_blocker.h
+++ b/chrome/browser/ui/ash/login/input_events_blocker.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 CHROME_BROWSER_ASH_LOGIN_UI_INPUT_EVENTS_BLOCKER_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_INPUT_EVENTS_BLOCKER_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_INPUT_EVENTS_BLOCKER_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_INPUT_EVENTS_BLOCKER_H_
 
 #include "ui/events/event_handler.h"
 
@@ -29,4 +29,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_INPUT_EVENTS_BLOCKER_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_INPUT_EVENTS_BLOCKER_H_
diff --git a/chrome/browser/ash/login/ui/kiosk_app_menu_controller.cc b/chrome/browser/ui/ash/login/kiosk_app_menu_controller.cc
similarity index 97%
rename from chrome/browser/ash/login/ui/kiosk_app_menu_controller.cc
rename to chrome/browser/ui/ash/login/kiosk_app_menu_controller.cc
index 874be01..f37e700 100644
--- a/chrome/browser/ash/login/ui/kiosk_app_menu_controller.cc
+++ b/chrome/browser/ui/ash/login/kiosk_app_menu_controller.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/kiosk_app_menu_controller.h"
+#include "chrome/browser/ui/ash/login/kiosk_app_menu_controller.h"
 
 #include <string>
 #include <vector>
@@ -18,7 +18,7 @@
 #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
 #include "chrome/browser/ash/app_mode/kiosk_controller.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
 #include "extensions/grit/extensions_browser_resources.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/chrome/browser/ash/login/ui/kiosk_app_menu_controller.h b/chrome/browser/ui/ash/login/kiosk_app_menu_controller.h
similarity index 87%
rename from chrome/browser/ash/login/ui/kiosk_app_menu_controller.h
rename to chrome/browser/ui/ash/login/kiosk_app_menu_controller.h
index 9eec4d8..ceebeb7b 100644
--- a/chrome/browser/ash/login/ui/kiosk_app_menu_controller.h
+++ b/chrome/browser/ui/ash/login/kiosk_app_menu_controller.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 CHROME_BROWSER_ASH_LOGIN_UI_KIOSK_APP_MENU_CONTROLLER_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_KIOSK_APP_MENU_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_KIOSK_APP_MENU_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_KIOSK_APP_MENU_CONTROLLER_H_
 
 #include "ash/public/cpp/kiosk_app_menu.h"
 #include "base/memory/weak_ptr.h"
@@ -45,4 +45,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_KIOSK_APP_MENU_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_KIOSK_APP_MENU_CONTROLLER_H_
diff --git a/chrome/browser/ash/login/ui/login_display_host.cc b/chrome/browser/ui/ash/login/login_display_host.cc
similarity index 92%
rename from chrome/browser/ash/login/ui/login_display_host.cc
rename to chrome/browser/ui/ash/login/login_display_host.cc
index 922092bd..cca6597d 100644
--- a/chrome/browser/ash/login/ui/login_display_host.cc
+++ b/chrome/browser/ui/ash/login/login_display_host.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 
 #include "base/functional/callback.h"
 #include "base/task/single_thread_task_runner.h"
@@ -49,8 +49,9 @@
 }
 
 void LoginDisplayHost::NotifyWizardCreated() {
-  if (on_wizard_controller_created_for_tests_)
+  if (on_wizard_controller_created_for_tests_) {
     on_wizard_controller_created_for_tests_.Run();
+  }
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/ui/login_display_host.h b/chrome/browser/ui/ash/login/login_display_host.h
similarity index 97%
rename from chrome/browser/ash/login/ui/login_display_host.h
rename to chrome/browser/ui/ash/login/login_display_host.h
index ce024a2..83ceca42 100644
--- a/chrome/browser/ash/login/ui/login_display_host.h
+++ b/chrome/browser/ui/ash/login/login_display_host.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_H_
 
 #include <optional>
 #include <string>
@@ -15,7 +15,7 @@
 #include "base/observer_list_types.h"
 #include "chrome/browser/ash/customization/customization_document.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "components/user_manager/user_type.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/widget/widget.h"
@@ -265,4 +265,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_H_
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.cc b/chrome/browser/ui/ash/login/login_display_host_common.cc
similarity index 99%
rename from chrome/browser/ash/login/ui/login_display_host_common.cc
rename to chrome/browser/ui/ash/login/login_display_host_common.cc
index 52bfff7..689f7a6 100644
--- a/chrome/browser/ash/login/ui/login_display_host_common.cc
+++ b/chrome/browser/ui/ash/login/login_display_host_common.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_display_host_common.h"
+#include "chrome/browser/ui/ash/login/login_display_host_common.h"
 
 #include <cstdint>
 #include <memory>
@@ -43,8 +43,6 @@
 #include "chrome/browser/ash/login/screens/saml_confirm_password_screen.h"
 #include "chrome/browser/ash/login/screens/signin_fatal_error_screen.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/login_feedback.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/nearby/quick_start_connectivity_service_factory.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -57,6 +55,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/chrome_device_id_helper.h"
+#include "chrome/browser/ui/ash/login/login_feedback.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "chrome/browser/ui/ash/system/system_tray_client_impl.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/webui/ash/diagnostics_dialog/diagnostics_dialog.h"
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.h b/chrome/browser/ui/ash/login/login_display_host_common.h
similarity index 92%
rename from chrome/browser/ash/login/ui/login_display_host_common.h
rename to chrome/browser/ui/ash/login/login_display_host_common.h
index 753cadf..d5f6faf8 100644
--- a/chrome/browser/ash/login/ui/login_display_host_common.h
+++ b/chrome/browser/ui/ash/login/login_display_host_common.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_COMMON_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_COMMON_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_COMMON_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_COMMON_H_
 
 #include <memory>
 #include <optional>
@@ -13,11 +13,11 @@
 #include "ash/public/cpp/login_accelerators.h"
 #include "base/callback_list.h"
 #include "chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h"
-#include "chrome/browser/ash/login/ui/kiosk_app_menu_controller.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_ui_pref_controller.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
+#include "chrome/browser/ui/ash/login/kiosk_app_menu_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_ui_pref_controller.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/user_manager/user_type.h"
@@ -168,4 +168,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_COMMON_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_COMMON_H_
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.cc b/chrome/browser/ui/ash/login/login_display_host_mojo.cc
similarity index 99%
rename from chrome/browser/ash/login/ui/login_display_host_mojo.cc
rename to chrome/browser/ui/ash/login/login_display_host_mojo.cc
index 6d6cb3e3..3c341cd 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/ui/ash/login/login_display_host_mojo.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
+#include "chrome/browser/ui/ash/login/login_display_host_mojo.h"
 
 #include <optional>
 #include <utility>
@@ -46,16 +46,16 @@
 #include "chrome/browser/ash/login/screens/gaia_screen.h"
 #include "chrome/browser/ash/login/screens/user_selection_screen.h"
 #include "chrome/browser/ash/login/security_token_session_controller.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_common.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/certificate_provider/certificate_provider_service.h"
 #include "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_common.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "chrome/browser/ui/ash/system/system_tray_client_impl.h"
 #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
 #include "chrome/browser/ui/webui/ash/login/enable_adb_sideloading_screen_handler.h"
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.h b/chrome/browser/ui/ash/login/login_display_host_mojo.h
similarity index 96%
rename from chrome/browser/ash/login/ui/login_display_host_mojo.h
rename to chrome/browser/ui/ash/login/login_display_host_mojo.h
index c68615e..9fd592a 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.h
+++ b/chrome/browser/ui/ash/login/login_display_host_mojo.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_MOJO_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_MOJO_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_MOJO_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_MOJO_H_
 
 #include <memory>
 #include <optional>
@@ -18,9 +18,9 @@
 #include "chrome/browser/ash/login/challenge_response_auth_keys_loader.h"
 #include "chrome/browser/ash/login/screens/user_selection_screen.h"
 #include "chrome/browser/ash/login/security_token_pin_dialog_host_login_impl.h"
-#include "chrome/browser/ash/login/ui/login_display_host_common.h"
-#include "chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h"
+#include "chrome/browser/ui/ash/login/login_display_host_common.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
+#include "chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chromeos/ash/components/login/auth/auth_performer.h"
 #include "chromeos/ash/components/login/auth/auth_status_consumer.h"
@@ -266,4 +266,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_MOJO_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_MOJO_H_
diff --git a/chrome/browser/ash/login/ui/login_display_host_webui.cc b/chrome/browser/ui/ash/login/login_display_host_webui.cc
similarity index 99%
rename from chrome/browser/ash/login/ui/login_display_host_webui.cc
rename to chrome/browser/ui/ash/login/login_display_host_webui.cc
index fd640d2a..c0708a2 100644
--- a/chrome/browser/ash/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/ui/ash/login/login_display_host_webui.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 
 #include <memory>
 #include <utility>
@@ -45,9 +45,6 @@
 #include "chrome/browser/ash/login/login_wizard.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/input_events_blocker.h"
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/net/delay_network_call.h"
@@ -63,6 +60,9 @@
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
+#include "chrome/browser/ui/ash/login/input_events_blocker.h"
+#include "chrome/browser/ui/ash/login/login_display_host_mojo.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/ash/system/system_tray_client_impl.h"
 #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
diff --git a/chrome/browser/ash/login/ui/login_display_host_webui.h b/chrome/browser/ui/ash/login/login_display_host_webui.h
similarity index 97%
rename from chrome/browser/ash/login/ui/login_display_host_webui.h
rename to chrome/browser/ui/ash/login/login_display_host_webui.h
index 53fe0611..a61d9a6 100644
--- a/chrome/browser/ash/login/ui/login_display_host_webui.h
+++ b/chrome/browser/ui/ash/login/login_display_host_webui.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_WEBUI_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_WEBUI_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_WEBUI_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_WEBUI_H_
 
 #include <stdint.h>
 
@@ -20,8 +20,8 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/oobe_configuration.h"
-#include "chrome/browser/ash/login/ui/login_display_host_common.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host_common.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chromeos/ash/components/audio/cras_audio_handler.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
@@ -288,4 +288,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_DISPLAY_HOST_WEBUI_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_DISPLAY_HOST_WEBUI_H_
diff --git a/chrome/browser/ash/login/ui/login_feedback.cc b/chrome/browser/ui/ash/login/login_feedback.cc
similarity index 96%
rename from chrome/browser/ash/login/ui/login_feedback.cc
rename to chrome/browser/ui/ash/login/login_feedback.cc
index 90342e56..7099676 100644
--- a/chrome/browser/ash/login/ui/login_feedback.cc
+++ b/chrome/browser/ui/ash/login/login_feedback.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_feedback.h"
+#include "chrome/browser/ui/ash/login/login_feedback.h"
 
 #include <utility>
 
diff --git a/chrome/browser/ash/login/ui/login_feedback.h b/chrome/browser/ui/ash/login/login_feedback.h
similarity index 85%
rename from chrome/browser/ash/login/ui/login_feedback.h
rename to chrome/browser/ui/ash/login/login_feedback.h
index 77784b0..52f7350 100644
--- a/chrome/browser/ash/login/ui/login_feedback.h
+++ b/chrome/browser/ui/ash/login/login_feedback.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_FEEDBACK_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_FEEDBACK_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_FEEDBACK_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_FEEDBACK_H_
 
 #include <string>
 
@@ -37,4 +37,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_FEEDBACK_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_FEEDBACK_H_
diff --git a/chrome/browser/ash/login/ui/login_feedback_browsertest.cc b/chrome/browser/ui/ash/login/login_feedback_browsertest.cc
similarity index 98%
rename from chrome/browser/ash/login/ui/login_feedback_browsertest.cc
rename to chrome/browser/ui/ash/login/login_feedback_browsertest.cc
index b7e1e63..a3fa9e84 100644
--- a/chrome/browser/ash/login/ui/login_feedback_browsertest.cc
+++ b/chrome/browser/ui/ash/login/login_feedback_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_feedback.h"
+#include "chrome/browser/ui/ash/login/login_feedback.h"
 
 #include <memory>
 
diff --git a/chrome/browser/ui/ash/login/login_screen_client_impl.cc b/chrome/browser/ui/ash/login/login_screen_client_impl.cc
index eb53154..0cee8744 100644
--- a/chrome/browser/ui/ash/login/login_screen_client_impl.cc
+++ b/chrome/browser/ui/ash/login/login_screen_client_impl.cc
@@ -27,11 +27,11 @@
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/reauth_stats.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_metrics.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_dialogs.h"
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/BUILD.gn b/chrome/browser/ui/ash/login/login_screen_extension_ui/BUILD.gn
similarity index 100%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/BUILD.gn
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/BUILD.gn
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.cc b/chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.cc
similarity index 91%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.cc
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.cc
index 247cd6b..d09eb54 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.cc
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h"
 
 #include <memory>
 
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h b/chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h
similarity index 81%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h
index d97f8a79..4ab27e3 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_CREATE_OPTIONS_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_CREATE_OPTIONS_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_CREATE_OPTIONS_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_CREATE_OPTIONS_H_
 
 #include <string>
 
@@ -33,4 +33,4 @@
 }  // namespace login_screen_extension_ui
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_CREATE_OPTIONS_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_CREATE_OPTIONS_H_
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.cc b/chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.cc
similarity index 95%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.cc
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.cc
index fa1d8cb..3b794d6 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.cc
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_message_handler.h"
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h b/chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.h
similarity index 91%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.h
index 78c1c702..355cbac 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_DIALOG_DELEGATE_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_DIALOG_DELEGATE_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_DIALOG_DELEGATE_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_DIALOG_DELEGATE_H_
 
 #include <string>
 #include <vector>
@@ -72,4 +72,4 @@
 }  // namespace login_screen_extension_ui
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_DIALOG_DELEGATE_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_DIALOG_DELEGATE_H_
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate_unittest.cc b/chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate_unittest.cc
similarity index 95%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate_unittest.cc
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate_unittest.cc
index ab141f42..58d05f5 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate_unittest.cc
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.h"
 
 #include <memory>
 
@@ -10,7 +10,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/extensions/login_screen_ui/ui_handler.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.cc b/chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.cc
similarity index 92%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.cc
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.cc
index b1d1d88..a3486fb 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.cc
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.h"
 
 #include "ash/public/cpp/login_screen.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.h"
 #include "content/public/browser/browser_context.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.h b/chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.h
similarity index 89%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.h
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.h
index 745604e..45488b5 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.h
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_LOGIN_WEB_VIEW_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_LOGIN_WEB_VIEW_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_LOGIN_WEB_VIEW_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_LOGIN_WEB_VIEW_H_
 
 #include <memory>
 
@@ -53,4 +53,4 @@
 }  // namespace login_screen_extension_ui
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_LOGIN_WEB_VIEW_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_LOGIN_WEB_VIEW_H_
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view_unittest.cc b/chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view_unittest.cc
similarity index 94%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view_unittest.cc
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view_unittest.cc
index 80378bd..ef13146 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view_unittest.cc
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view_unittest.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.h"
 
 #include <memory>
 
 #include "base/functional/callback_helpers.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.h"
 #include "chrome/browser/ui/ash/login/test_login_screen.h"
 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
 #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/window.cc b/chrome/browser/ui/ash/login/login_screen_extension_ui/window.cc
similarity index 87%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/window.cc
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/window.cc
index bf41d8571..13b3d7cd5 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/window.cc
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/window.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/window.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/window.h"
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/utility/wm_util.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/create_options.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/dialog_delegate.h"
-#include "chrome/browser/ash/login/ui/login_screen_extension_ui/login_web_view.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/create_options.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/dialog_delegate.h"
+#include "chrome/browser/ui/ash/login/login_screen_extension_ui/login_web_view.h"
 #include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
 #include "ui/views/widget/widget.h"
 
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/window.h b/chrome/browser/ui/ash/login/login_screen_extension_ui/window.h
similarity index 88%
rename from chrome/browser/ash/login/ui/login_screen_extension_ui/window.h
rename to chrome/browser/ui/ash/login/login_screen_extension_ui/window.h
index f0d8461..67b7e14 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/window.h
+++ b/chrome/browser/ui/ash/login/login_screen_extension_ui/window.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_WINDOW_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_WINDOW_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_WINDOW_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_WINDOW_H_
 
 #include <memory>
 
@@ -57,4 +57,4 @@
 }  // namespace login_screen_extension_ui
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_SCREEN_EXTENSION_UI_WINDOW_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_SCREEN_EXTENSION_UI_WINDOW_H_
diff --git a/chrome/browser/ash/login/ui/login_ui_pref_controller.cc b/chrome/browser/ui/ash/login/login_ui_pref_controller.cc
similarity index 97%
rename from chrome/browser/ash/login/ui/login_ui_pref_controller.cc
rename to chrome/browser/ui/ash/login/login_ui_pref_controller.cc
index dec33ae..7057c84 100644
--- a/chrome/browser/ash/login/ui/login_ui_pref_controller.cc
+++ b/chrome/browser/ui/ash/login/login_ui_pref_controller.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_ui_pref_controller.h"
+#include "chrome/browser/ui/ash/login/login_ui_pref_controller.h"
 
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/geolocation_access_level.h"
diff --git a/chrome/browser/ash/login/ui/login_ui_pref_controller.h b/chrome/browser/ui/ash/login/login_ui_pref_controller.h
similarity index 87%
rename from chrome/browser/ash/login/ui/login_ui_pref_controller.h
rename to chrome/browser/ui/ash/login/login_ui_pref_controller.h
index d82aa22..2d8b9dfd 100644
--- a/chrome/browser/ash/login/ui/login_ui_pref_controller.h
+++ b/chrome/browser/ui/ash/login/login_ui_pref_controller.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_UI_PREF_CONTROLLER_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_UI_PREF_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_UI_PREF_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_UI_PREF_CONTROLLER_H_
 
 #include "base/memory/weak_ptr.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -42,4 +42,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_UI_PREF_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_UI_PREF_CONTROLLER_H_
diff --git a/chrome/browser/ash/login/ui/login_ui_pref_controller_browsertest.cc b/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc
similarity index 98%
rename from chrome/browser/ash/login/ui/login_ui_pref_controller_browsertest.cc
rename to chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc
index 7a51d3e..4fc34bd 100644
--- a/chrome/browser/ash/login/ui/login_ui_pref_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_ui_pref_controller.h"
+#include "chrome/browser/ui/ash/login/login_ui_pref_controller.h"
 
 #include "ash/constants/ash_pref_names.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
diff --git a/chrome/browser/ash/login/ui/login_web_dialog.cc b/chrome/browser/ui/ash/login/login_web_dialog.cc
similarity index 94%
rename from chrome/browser/ash/login/ui/login_web_dialog.cc
rename to chrome/browser/ui/ash/login/login_web_dialog.cc
index c022f116..db09e44 100644
--- a/chrome/browser/ash/login/ui/login_web_dialog.cc
+++ b/chrome/browser/ui/ash/login/login_web_dialog.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_web_dialog.h"
+#include "chrome/browser/ui/ash/login/login_web_dialog.h"
 
 #include "base/containers/circular_deque.h"
 #include "base/lazy_instance.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/login/helper.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "content/public/browser/browser_context.h"
@@ -43,8 +43,9 @@
                                const std::u16string& title,
                                const GURL& url)
     : browser_context_(browser_context), parent_window_(parent_window) {
-  if (!parent_window_ && LoginDisplayHost::default_host())
+  if (!parent_window_ && LoginDisplayHost::default_host()) {
     parent_window_ = LoginDisplayHost::default_host()->GetNativeWindow();
+  }
   LOG_IF(WARNING, !parent_window_)
       << "No parent window. Dialog sizes could be wrong";
 
@@ -104,8 +105,9 @@
                                      bool* out_close_dialog) {
   *out_close_dialog = true;
 
-  if (GetCurrentWebContents() == source)
+  if (GetCurrentWebContents() == source) {
     g_web_contents_stack.Pointer()->pop_front();
+  }
 }
 
 bool LoginWebDialog::HandleOpenURLFromTab(
@@ -125,8 +127,9 @@
 
 bool LoginWebDialog::MaybeCloseWindow(WebDialogDelegate& delegate,
                                       const ui::Accelerator& accelerator) {
-  if (!dialog_window_)
+  if (!dialog_window_) {
     return false;
+  }
 
   views::Widget::GetWidgetForNativeWindow(dialog_window_)->Close();
   return true;
diff --git a/chrome/browser/ash/login/ui/login_web_dialog.h b/chrome/browser/ui/ash/login/login_web_dialog.h
similarity index 92%
rename from chrome/browser/ash/login/ui/login_web_dialog.h
rename to chrome/browser/ui/ash/login/login_web_dialog.h
index 4a9805f7..5f5ec4a 100644
--- a/chrome/browser/ash/login/ui/login_web_dialog.h
+++ b/chrome/browser/ui/ash/login/login_web_dialog.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 CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_WEB_DIALOG_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_WEB_DIALOG_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_WEB_DIALOG_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_WEB_DIALOG_H_
 
 #include <string>
 #include <vector>
@@ -69,4 +69,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_LOGIN_WEB_DIALOG_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_LOGIN_WEB_DIALOG_H_
diff --git a/chrome/browser/ash/login/ui/login_web_dialog_browsertest.cc b/chrome/browser/ui/ash/login/login_web_dialog_browsertest.cc
similarity index 97%
rename from chrome/browser/ash/login/ui/login_web_dialog_browsertest.cc
rename to chrome/browser/ui/ash/login/login_web_dialog_browsertest.cc
index 538c0895..e6ebb049 100644
--- a/chrome/browser/ash/login/ui/login_web_dialog_browsertest.cc
+++ b/chrome/browser/ui/ash/login/login_web_dialog_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/login_web_dialog.h"
+#include "chrome/browser/ui/ash/login/login_web_dialog.h"
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/ash/login/ui/mock_login_display_host.cc b/chrome/browser/ui/ash/login/mock_login_display_host.cc
similarity index 81%
rename from chrome/browser/ash/login/ui/mock_login_display_host.cc
rename to chrome/browser/ui/ash/login/mock_login_display_host.cc
index ae32f82..6560a67 100644
--- a/chrome/browser/ash/login/ui/mock_login_display_host.cc
+++ b/chrome/browser/ui/ash/login/mock_login_display_host.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/mock_login_display_host.h"
+#include "chrome/browser/ui/ash/login/mock_login_display_host.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/login/ui/mock_login_display_host.h b/chrome/browser/ui/ash/login/mock_login_display_host.h
similarity index 92%
rename from chrome/browser/ash/login/ui/mock_login_display_host.h
rename to chrome/browser/ui/ash/login/mock_login_display_host.h
index 6040da0..a5f21cd 100644
--- a/chrome/browser/ash/login/ui/mock_login_display_host.h
+++ b/chrome/browser/ui/ash/login/mock_login_display_host.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 CHROME_BROWSER_ASH_LOGIN_UI_MOCK_LOGIN_DISPLAY_HOST_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_MOCK_LOGIN_DISPLAY_HOST_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_MOCK_LOGIN_DISPLAY_HOST_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_MOCK_LOGIN_DISPLAY_HOST_H_
 
 #include <optional>
 #include <string>
@@ -11,8 +11,8 @@
 #include "ash/public/cpp/login_accelerators.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_types.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "components/user_manager/user_type.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -101,4 +101,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_MOCK_LOGIN_DISPLAY_HOST_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_MOCK_LOGIN_DISPLAY_HOST_H_
diff --git a/chrome/browser/ash/login/ui/mock_signin_ui.cc b/chrome/browser/ui/ash/login/mock_signin_ui.cc
similarity index 82%
rename from chrome/browser/ash/login/ui/mock_signin_ui.cc
rename to chrome/browser/ui/ash/login/mock_signin_ui.cc
index d4ae968..2441ab5 100644
--- a/chrome/browser/ash/login/ui/mock_signin_ui.cc
+++ b/chrome/browser/ui/ash/login/mock_signin_ui.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/mock_signin_ui.h"
+#include "chrome/browser/ui/ash/login/mock_signin_ui.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/login/ui/mock_signin_ui.h b/chrome/browser/ui/ash/login/mock_signin_ui.h
similarity index 90%
rename from chrome/browser/ash/login/ui/mock_signin_ui.h
rename to chrome/browser/ui/ash/login/mock_signin_ui.h
index 7489d45..9ad9dda 100644
--- a/chrome/browser/ash/login/ui/mock_signin_ui.h
+++ b/chrome/browser/ui/ash/login/mock_signin_ui.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ASH_LOGIN_UI_MOCK_SIGNIN_UI_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_MOCK_SIGNIN_UI_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_MOCK_SIGNIN_UI_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_MOCK_SIGNIN_UI_H_
 
 #include <memory>
 
-#include "chrome/browser/ash/login/ui/signin_ui.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "components/login/base_screen_handler_utils.h"
 #include "components/prefs/pref_service.h"
@@ -62,4 +62,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_MOCK_SIGNIN_UI_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_MOCK_SIGNIN_UI_H_
diff --git a/chrome/browser/ash/login/ui/oobe_dialog_size_utils.cc b/chrome/browser/ui/ash/login/oobe_dialog_size_utils.cc
similarity index 97%
rename from chrome/browser/ash/login/ui/oobe_dialog_size_utils.cc
rename to chrome/browser/ui/ash/login/oobe_dialog_size_utils.cc
index c73fe08..f483f91 100644
--- a/chrome/browser/ash/login/ui/oobe_dialog_size_utils.cc
+++ b/chrome/browser/ui/ash/login/oobe_dialog_size_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 
 #include "ash/public/cpp/shelf_config.h"
 #include "ui/display/display.h"
diff --git a/chrome/browser/ash/login/ui/oobe_dialog_size_utils.h b/chrome/browser/ui/ash/login/oobe_dialog_size_utils.h
similarity index 89%
rename from chrome/browser/ash/login/ui/oobe_dialog_size_utils.h
rename to chrome/browser/ui/ash/login/oobe_dialog_size_utils.h
index c64d657..cb2969b 100644
--- a/chrome/browser/ash/login/ui/oobe_dialog_size_utils.h
+++ b/chrome/browser/ui/ash/login/oobe_dialog_size_utils.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 CHROME_BROWSER_ASH_LOGIN_UI_OOBE_DIALOG_SIZE_UTILS_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_OOBE_DIALOG_SIZE_UTILS_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_OOBE_DIALOG_SIZE_UTILS_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_OOBE_DIALOG_SIZE_UTILS_H_
 
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
@@ -44,4 +44,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_OOBE_DIALOG_SIZE_UTILS_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_OOBE_DIALOG_SIZE_UTILS_H_
diff --git a/chrome/browser/ash/login/ui/oobe_dialog_size_utils_unittest.cc b/chrome/browser/ui/ash/login/oobe_dialog_size_utils_unittest.cc
similarity index 98%
rename from chrome/browser/ash/login/ui/oobe_dialog_size_utils_unittest.cc
rename to chrome/browser/ui/ash/login/oobe_dialog_size_utils_unittest.cc
index ca19545..f45c287 100644
--- a/chrome/browser/ash/login/ui/oobe_dialog_size_utils_unittest.cc
+++ b/chrome/browser/ui/ash/login/oobe_dialog_size_utils_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 
 #include <stddef.h>
 
diff --git a/chrome/browser/ash/login/ui/oobe_dialog_util_impl.cc b/chrome/browser/ui/ash/login/oobe_dialog_util_impl.cc
similarity index 85%
rename from chrome/browser/ash/login/ui/oobe_dialog_util_impl.cc
rename to chrome/browser/ui/ash/login/oobe_dialog_util_impl.cc
index a64ac125..63f2fbf 100644
--- a/chrome/browser/ash/login/ui/oobe_dialog_util_impl.cc
+++ b/chrome/browser/ui/ash/login/oobe_dialog_util_impl.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/oobe_dialog_util_impl.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_util_impl.h"
 
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 
 namespace ash {
 
diff --git a/chrome/browser/ash/login/ui/oobe_dialog_util_impl.h b/chrome/browser/ui/ash/login/oobe_dialog_util_impl.h
similarity index 80%
rename from chrome/browser/ash/login/ui/oobe_dialog_util_impl.h
rename to chrome/browser/ui/ash/login/oobe_dialog_util_impl.h
index 1a5cf47..097413c 100644
--- a/chrome/browser/ash/login/ui/oobe_dialog_util_impl.h
+++ b/chrome/browser/ui/ash/login/oobe_dialog_util_impl.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 CHROME_BROWSER_ASH_LOGIN_UI_OOBE_DIALOG_UTIL_IMPL_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_OOBE_DIALOG_UTIL_IMPL_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_OOBE_DIALOG_UTIL_IMPL_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_OOBE_DIALOG_UTIL_IMPL_H_
 
 #include "ash/public/cpp/oobe_dialog_util.h"
 
@@ -26,4 +26,4 @@
 };
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_OOBE_DIALOG_UTIL_IMPL_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_OOBE_DIALOG_UTIL_IMPL_H_
diff --git a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.cc
similarity index 95%
rename from chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc
rename to chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.cc
index 01ea94c..b880f1a1 100644
--- a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.cc
@@ -7,7 +7,7 @@
 #pragma allow_unsafe_buffers
 #endif
 
-#include "chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h"
+#include "chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.h"
 
 #include <memory>
 #include <utility>
@@ -21,11 +21,11 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/utility/wm_util.h"
 #include "base/memory/raw_ptr.h"
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
+#include "chrome/browser/ui/ash/login/login_display_host_mojo.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 #include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
@@ -133,8 +133,9 @@
 
   OobeUI* GetOobeUI() {
     content::WebUI* webui = web_contents()->GetWebUI();
-    if (webui)
+    if (webui) {
       return static_cast<OobeUI*>(webui->GetController());
+    }
     return nullptr;
   }
 
@@ -192,16 +193,18 @@
   ~LayoutWidgetDelegateView() override { delete dialog_delegate_; }
 
   void SetFullscreen(bool value) {
-    if (fullscreen_ == value)
+    if (fullscreen_ == value) {
       return;
+    }
     fullscreen_ = value;
     OnPropertyChanged(&fullscreen_, views::kPropertyEffectsLayout);
   }
   bool GetFullscreen() const { return fullscreen_; }
 
   void SetHasShelf(bool value) {
-    if (has_shelf_ == value)
+    if (has_shelf_ == value) {
       return;
+    }
     has_shelf_ = value;
     OnPropertyChanged(&has_shelf_, views::kPropertyEffectsLayout);
   }
@@ -260,8 +263,9 @@
   keyboard_observer_.Observe(ChromeKeyboardControllerClient::Get());
 
   for (size_t i = 0; i < kLoginAcceleratorDataLength; ++i) {
-    if (kLoginAcceleratorData[i].global)
+    if (kLoginAcceleratorData[i].global) {
       continue;
+    }
     if (!(kLoginAcceleratorData[i].scope & (kScopeLogin | kScopeLock))) {
       continue;
     }
@@ -350,8 +354,9 @@
     LoginScreen::Get()->GetModel()->NotifyOobeDialogState(state_);
   }
 
-  if (should_display_captive_portal_)
+  if (should_display_captive_portal_) {
     GetOobeUI()->GetErrorScreen()->FixCaptivePortal();
+  }
 }
 
 void OobeUIDialogDelegate::ShowFullScreen() {
@@ -361,15 +366,17 @@
 
 void OobeUIDialogDelegate::Hide() {
   scoped_system_tray_observer_.Reset();
-  if (!widget_)
+  if (!widget_) {
     return;
+  }
   widget_->Hide();
   SetState(OobeDialogState::HIDDEN);
 }
 
 void OobeUIDialogDelegate::Close() {
-  if (!widget_)
+  if (!widget_) {
     return;
+  }
   SetState(OobeDialogState::HIDDEN);
 
   // We do not call LoginScreen::NotifyOobeDialogVisibility here, because this
@@ -379,22 +386,25 @@
 }
 
 void OobeUIDialogDelegate::SetState(OobeDialogState state) {
-  if (!widget_ || state_ == state)
+  if (!widget_ || state_ == state) {
     return;
+  }
 
   state_ = state;
 
   // Gaia WebUI is preloaded, so it's possible for WebUI to send state updates
   // while the widget is not visible. Defer the state update until Show().
-  if (!widget_->IsVisible() && state_ != OobeDialogState::HIDDEN)
+  if (!widget_->IsVisible() && state_ != OobeDialogState::HIDDEN) {
     return;
+  }
 
   LoginScreen::Get()->GetModel()->NotifyOobeDialogState(state_);
 }
 
 OobeUI* OobeUIDialogDelegate::GetOobeUI() const {
-  if (dialog_view_)
+  if (dialog_view_) {
     return dialog_view_->GetOobeUI();
+  }
   return nullptr;
 }
 
@@ -418,8 +428,9 @@
   // TODO(crbug.com/40561667): Adding necessary accelerators.
   std::vector<ui::Accelerator> output;
 
-  for (const auto& pair : accel_map_)
+  for (const auto& pair : accel_map_) {
     output.push_back(pair.first);
+  }
 
   return output;
 }
@@ -427,16 +438,19 @@
 bool OobeUIDialogDelegate::AcceleratorPressed(
     const ui::Accelerator& accelerator) {
   auto entry = accel_map_.find(accelerator);
-  if (entry == accel_map_.end())
+  if (entry == accel_map_.end()) {
     return false;
-  if (controller_)
+  }
+  if (controller_) {
     return controller_->HandleAccelerator(entry->second);
+  }
   return false;
 }
 
 void OobeUIDialogDelegate::OnViewBoundsChanged(views::View* observed_view) {
-  if (!widget_)
+  if (!widget_) {
     return;
+  }
   GetOobeUI()->GetCoreOobe()->UpdateClientAreaSize(
       layout_view_->GetContentsBounds().size());
 }
@@ -448,8 +462,9 @@
 }
 
 void OobeUIDialogDelegate::OnKeyboardVisibilityChanged(bool visible) {
-  if (!widget_)
+  if (!widget_) {
     return;
+  }
   layout_view_->SetHasShelf(!visible);
 }
 
@@ -473,8 +488,9 @@
 }
 
 void OobeUIDialogDelegate::OnFocusLeavingSystemTray(bool reverse) {
-  if (dialog_view_)
+  if (dialog_view_) {
     dialog_view_->AboutToRequestFocusFromTabTraversal(reverse);
+  }
 }
 
 web_modal::WebContentsModalDialogHost*
diff --git a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h b/chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.h
similarity index 96%
rename from chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h
rename to chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.h
index 1fa75237..299e6b7 100644
--- a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h
+++ b/chrome/browser/ui/ash/login/oobe_ui_dialog_delegate.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 CHROME_BROWSER_ASH_LOGIN_UI_OOBE_UI_DIALOG_DELEGATE_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_OOBE_UI_DIALOG_DELEGATE_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_OOBE_UI_DIALOG_DELEGATE_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_OOBE_UI_DIALOG_DELEGATE_H_
 
 #include <string>
 
@@ -174,4 +174,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_OOBE_UI_DIALOG_DELEGATE_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_OOBE_UI_DIALOG_DELEGATE_H_
diff --git a/chrome/browser/ash/login/ui/signin_ui.h b/chrome/browser/ui/ash/login/signin_ui.h
similarity index 95%
rename from chrome/browser/ash/login/ui/signin_ui.h
rename to chrome/browser/ui/ash/login/signin_ui.h
index 8fd6cb3..952d219 100644
--- a/chrome/browser/ash/login/ui/signin_ui.h
+++ b/chrome/browser/ui/ash/login/signin_ui.h
@@ -1,8 +1,8 @@
 // Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-#ifndef CHROME_BROWSER_ASH_LOGIN_UI_SIGNIN_UI_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_SIGNIN_UI_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_SIGNIN_UI_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_SIGNIN_UI_H_
 
 #include <memory>
 
@@ -95,4 +95,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_SIGNIN_UI_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_SIGNIN_UI_H_
diff --git a/chrome/browser/ash/login/ui/simple_web_view_dialog.cc b/chrome/browser/ui/ash/login/simple_web_view_dialog.cc
similarity index 96%
rename from chrome/browser/ash/login/ui/simple_web_view_dialog.cc
rename to chrome/browser/ui/ash/login/simple_web_view_dialog.cc
index a976fe57..5535c2a 100644
--- a/chrome/browser/ash/login/ui/simple_web_view_dialog.cc
+++ b/chrome/browser/ui/ash/login/simple_web_view_dialog.cc
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/simple_web_view_dialog.h"
+#include "chrome/browser/ui/ash/login/simple_web_view_dialog.h"
 
 #include <memory>
 
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/ash/login/helper.h"
-#include "chrome/browser/ash/login/ui/captive_portal_window_proxy.h"
 #include "chrome/browser/command_updater_impl.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/chrome_password_reuse_detection_manager_client.h"
 #include "chrome/browser/ssl/chrome_security_state_tab_helper.h"
 #include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/ash/login/captive_portal_window_proxy.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model_delegate.h"
@@ -116,8 +116,7 @@
 // SimpleWebViewDialog class ---------------------------------------------------
 
 SimpleWebViewDialog::SimpleWebViewDialog(Profile* profile)
-    : profile_(profile),
-      bubble_model_delegate_(new StubBubbleModelDelegate) {
+    : profile_(profile), bubble_model_delegate_(new StubBubbleModelDelegate) {
   command_updater_ = std::make_unique<CommandUpdaterImpl>(this);
   command_updater_->UpdateCommandEnabled(IDC_BACK, true);
   command_updater_->UpdateCommandEnabled(IDC_FORWARD, true);
@@ -128,8 +127,9 @@
 }
 
 SimpleWebViewDialog::~SimpleWebViewDialog() {
-  for (auto& observer : observers_)
+  for (auto& observer : observers_) {
     observer.OnHostDestroying();
+  }
 
   if (web_view_ && web_view_->web_contents()) {
     web_view_->web_contents()->SetDelegate(nullptr);
@@ -140,8 +140,9 @@
 }
 
 void SimpleWebViewDialog::StartLoad(const GURL& url) {
-  if (!web_view_container_)
+  if (!web_view_container_) {
     web_view_container_ = std::make_unique<views::WebView>(profile_);
+  }
   web_view_ = web_view_container_.get();
   web_view_->GetWebContents()->SetDelegate(this);
   web_view_->LoadInitialURL(url,
@@ -165,9 +166,10 @@
 
 void SimpleWebViewDialog::Init() {
   // Create the security state model that the location bar model needs.
-  if (web_view_->GetWebContents())
+  if (web_view_->GetWebContents()) {
     ChromeSecurityStateTabHelper::CreateForWebContents(
         web_view_->GetWebContents());
+  }
   location_bar_model_ = std::make_unique<LocationBarModelImpl>(
       this, content::kMaxURLDisplayChars);
 
@@ -327,7 +329,7 @@
   const ui::ThemeProvider* tp = GetThemeProvider();
 
   auto set_image_model = [=](views::ImageButton* button,
-                            views::Button::ButtonState state, int idr) {
+                             views::Button::ButtonState state, int idr) {
     gfx::ImageSkia* image = tp->GetImageSkiaNamed(idr);
     button->SetImageModel(state, image ? ui::ImageModel::FromImageSkia(*image)
                                        : ui::ImageModel());
diff --git a/chrome/browser/ash/login/ui/simple_web_view_dialog.h b/chrome/browser/ui/ash/login/simple_web_view_dialog.h
similarity index 96%
rename from chrome/browser/ash/login/ui/simple_web_view_dialog.h
rename to chrome/browser/ui/ash/login/simple_web_view_dialog.h
index 725bce6..4f4919fe 100644
--- a/chrome/browser/ash/login/ui/simple_web_view_dialog.h
+++ b/chrome/browser/ui/ash/login/simple_web_view_dialog.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 CHROME_BROWSER_ASH_LOGIN_UI_SIMPLE_WEB_VIEW_DIALOG_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_SIMPLE_WEB_VIEW_DIALOG_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_SIMPLE_WEB_VIEW_DIALOG_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_SIMPLE_WEB_VIEW_DIALOG_H_
 
 #include <memory>
 
@@ -130,4 +130,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_SIMPLE_WEB_VIEW_DIALOG_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_SIMPLE_WEB_VIEW_DIALOG_H_
diff --git a/chrome/browser/ash/login/ui/simple_web_view_dialog_browsertest.cc b/chrome/browser/ui/ash/login/simple_web_view_dialog_browsertest.cc
similarity index 98%
rename from chrome/browser/ash/login/ui/simple_web_view_dialog_browsertest.cc
rename to chrome/browser/ui/ash/login/simple_web_view_dialog_browsertest.cc
index 7faec9e5..b37b814 100644
--- a/chrome/browser/ash/login/ui/simple_web_view_dialog_browsertest.cc
+++ b/chrome/browser/ui/ash/login/simple_web_view_dialog_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/simple_web_view_dialog.h"
+#include "chrome/browser/ui/ash/login/simple_web_view_dialog.h"
 
 #include <memory>
 
diff --git a/chrome/browser/ash/login/ui/user_adding_screen.cc b/chrome/browser/ui/ash/login/user_adding_screen.cc
similarity index 90%
rename from chrome/browser/ash/login/ui/user_adding_screen.cc
rename to chrome/browser/ui/ash/login/user_adding_screen.cc
index a088303..eea0ea9 100644
--- a/chrome/browser/ash/login/ui/user_adding_screen.cc
+++ b/chrome/browser/ui/ash/login/user_adding_screen.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/singleton.h"
 #include "base/observer_list.h"
 #include "chrome/browser/ash/login/screens/user_selection_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_mojo.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen_input_methods_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_mojo.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen_input_methods_controller.h"
 #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
@@ -53,8 +53,9 @@
   // This triggers input method manager to filter login screen methods. This
   // should happen before setting user input method, which happens when focusing
   // user pod (triggered by SetSessionState)"
-  for (auto& observer : observers_)
+  for (auto& observer : observers_) {
     observer.OnBeforeUserAddingScreenStarted();
+  }
 
   session_manager::SessionManager::Get()->SetSessionState(
       session_manager::SessionState::LOGIN_SECONDARY);
@@ -93,8 +94,9 @@
 
   session_manager::SessionManager::Get()->SetSessionState(
       session_manager::SessionState::ACTIVE);
-  for (auto& observer : observers_)
+  for (auto& observer : observers_) {
     observer.OnUserAddingFinished();
+  }
 }
 
 // static
diff --git a/chrome/browser/ash/login/ui/user_adding_screen.h b/chrome/browser/ui/ash/login/user_adding_screen.h
similarity index 84%
rename from chrome/browser/ash/login/ui/user_adding_screen.h
rename to chrome/browser/ui/ash/login/user_adding_screen.h
index cb007f5..9107c99 100644
--- a/chrome/browser/ash/login/ui/user_adding_screen.h
+++ b/chrome/browser/ui/ash/login/user_adding_screen.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 CHROME_BROWSER_ASH_LOGIN_UI_USER_ADDING_SCREEN_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_USER_ADDING_SCREEN_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_USER_ADDING_SCREEN_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_USER_ADDING_SCREEN_H_
 
 namespace ash {
 
@@ -37,4 +37,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_USER_ADDING_SCREEN_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_USER_ADDING_SCREEN_H_
diff --git a/chrome/browser/ash/login/ui/user_adding_screen_browsertest.cc b/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc
similarity index 97%
rename from chrome/browser/ash/login/ui/user_adding_screen_browsertest.cc
rename to chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc
index 6ca5f443..a40f3e7 100644
--- a/chrome/browser/ash/login/ui/user_adding_screen_browsertest.cc
+++ b/chrome/browser/ui/ash/login/user_adding_screen_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/ash/login/user_adding_screen.h"
+
 #include <memory>
 
 #include "ash/constants/ash_features.h"
@@ -17,10 +19,9 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/ash/login/test/user_adding_screen_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
@@ -50,8 +51,9 @@
   }
 
   void WaitUntilUserAddingFinishedOrCancelled() {
-    if (finished_)
+    if (finished_) {
       return;
+    }
     run_loop_ = std::make_unique<base::RunLoop>();
     run_loop_->Run();
   }
@@ -59,8 +61,9 @@
   void OnUserAddingFinished() override {
     ++user_adding_finished_;
     finished_ = true;
-    if (run_loop_)
+    if (run_loop_) {
       run_loop_->Quit();
+    }
   }
 
   void OnBeforeUserAddingScreenStarted() override {
@@ -236,22 +239,25 @@
                         user_manager::MultiUserSignInPolicy::kUnrestricted));
   unlock_users = user_manager->GetUnlockUsers();
   ASSERT_EQ(unlock_users.size(), 3u);
-  for (int i = 0; i < 3; ++i)
+  for (int i = 0; i < 3; ++i) {
     EXPECT_EQ(users_in_session_order_[i], unlock_users[i]->GetAccountId());
+  }
 
   // This preference doesn't affect list of unlock users.
   prefs2->SetBoolean(prefs::kEnableAutoScreenLock, true);
   unlock_users = user_manager->GetUnlockUsers();
   ASSERT_EQ(unlock_users.size(), 3u);
-  for (int i = 0; i < 3; ++i)
+  for (int i = 0; i < 3; ++i) {
     EXPECT_EQ(users_in_session_order_[i], unlock_users[i]->GetAccountId());
+  }
 
   // Now one of the users is unable to unlock.
   prefs3->SetBoolean(ash::prefs::kAllowScreenLock, false);
   unlock_users = user_manager->GetUnlockUsers();
   ASSERT_EQ(unlock_users.size(), 2u);
-  for (int i = 0; i < 2; ++i)
+  for (int i = 0; i < 2; ++i) {
     EXPECT_EQ(users_in_session_order_[i], unlock_users[i]->GetAccountId());
+  }
   prefs3->SetBoolean(ash::prefs::kAllowScreenLock, true);
 
   // Now one of the users has not-allowed policy.
@@ -263,8 +269,9 @@
                         user_manager::MultiUserSignInPolicy::kNotAllowed));
   unlock_users = user_manager->GetUnlockUsers();
   ASSERT_EQ(unlock_users.size(), 2u);
-  for (int i = 0; i < 2; ++i)
+  for (int i = 0; i < 2; ++i) {
     EXPECT_EQ(users_in_session_order_[i], unlock_users[i]->GetAccountId());
+  }
 }
 
 // TODO(crbug.com/40059904) Disabled
diff --git a/chrome/browser/ash/login/ui/user_adding_screen_input_methods_controller.cc b/chrome/browser/ui/ash/login/user_adding_screen_input_methods_controller.cc
similarity index 89%
rename from chrome/browser/ash/login/ui/user_adding_screen_input_methods_controller.cc
rename to chrome/browser/ui/ash/login/user_adding_screen_input_methods_controller.cc
index bce0465..8b0b8bb9 100644
--- a/chrome/browser/ash/login/ui/user_adding_screen_input_methods_controller.cc
+++ b/chrome/browser/ui/ash/login/user_adding_screen_input_methods_controller.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ash/login/ui/user_adding_screen_input_methods_controller.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen_input_methods_controller.h"
 
 #include "components/user_manager/user_manager.h"
 
@@ -33,8 +33,10 @@
 }
 
 void UserAddingScreenInputMethodsController::OnUserAddingFinished() {
-  if (user_manager::UserManager::Get()->GetActiveUser() == active_user_on_show_)
+  if (user_manager::UserManager::Get()->GetActiveUser() ==
+      active_user_on_show_) {
     input_method::InputMethodManager::Get()->SetState(saved_ime_state_);
+  }
 
   saved_ime_state_.reset();
 }
diff --git a/chrome/browser/ash/login/ui/user_adding_screen_input_methods_controller.h b/chrome/browser/ui/ash/login/user_adding_screen_input_methods_controller.h
similarity index 83%
rename from chrome/browser/ash/login/ui/user_adding_screen_input_methods_controller.h
rename to chrome/browser/ui/ash/login/user_adding_screen_input_methods_controller.h
index fe25c0551..81e4dadf 100644
--- a/chrome/browser/ash/login/ui/user_adding_screen_input_methods_controller.h
+++ b/chrome/browser/ui/ash/login/user_adding_screen_input_methods_controller.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ASH_LOGIN_UI_USER_ADDING_SCREEN_INPUT_METHODS_CONTROLLER_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_USER_ADDING_SCREEN_INPUT_METHODS_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_USER_ADDING_SCREEN_INPUT_METHODS_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_USER_ADDING_SCREEN_INPUT_METHODS_CONTROLLER_H_
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "ui/base/ime/ash/input_method_manager.h"
 
 namespace user_manager {
@@ -42,4 +42,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_USER_ADDING_SCREEN_INPUT_METHODS_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_USER_ADDING_SCREEN_INPUT_METHODS_CONTROLLER_H_
diff --git a/chrome/browser/ash/login/ui/webui_login_view.cc b/chrome/browser/ui/ash/login/webui_login_view.cc
similarity index 96%
rename from chrome/browser/ash/login/ui/webui_login_view.cc
rename to chrome/browser/ui/ash/login/webui_login_view.cc
index f04b463..ffec047 100644
--- a/chrome/browser/ash/login/ui/webui_login_view.cc
+++ b/chrome/browser/ui/ash/login/webui_login_view.cc
@@ -7,7 +7,7 @@
 #pragma allow_unsafe_buffers
 #endif
 
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 
 #include <memory>
 #include <utility>
@@ -22,7 +22,6 @@
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
@@ -30,6 +29,7 @@
 #include "chrome/browser/renderer_preferences_util.h"
 #include "chrome/browser/safe_browsing/chrome_password_reuse_detection_manager_client.h"
 #include "chrome/browser/sessions/session_tab_helper_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
 #include "chrome/browser/ui/ash/system/system_tray_client_impl.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
@@ -152,12 +152,14 @@
 }
 
 WebUILoginView::~WebUILoginView() {
-  for (auto& observer : observer_list_)
+  for (auto& observer : observer_list_) {
     observer.OnHostDestroying();
+  }
 
   // TODO(crbug.com/1188526) - Improve the observation of the system tray
-  if (observing_system_tray_focus_ && LoginScreenClientImpl::HasInstance())
+  if (observing_system_tray_focus_ && LoginScreenClientImpl::HasInstance()) {
     LoginScreenClientImpl::Get()->RemoveSystemTrayObserver(this);
+  }
 
   // Clear any delegates we have set on the WebView.
   WebContents* web_contents = web_view_->GetWebContents();
@@ -208,8 +210,9 @@
 }
 
 void WebUILoginView::AddObserver(web_modal::ModalDialogHostObserver* observer) {
-  if (observer && !observer_list_.HasObserver(observer))
+  if (observer && !observer_list_.HasObserver(observer)) {
     observer_list_.AddObserver(observer);
+  }
 }
 
 void WebUILoginView::RemoveObserver(
@@ -219,10 +222,12 @@
 
 bool WebUILoginView::AcceleratorPressed(const ui::Accelerator& accelerator) {
   AccelMap::const_iterator entry = accel_map_.find(accelerator);
-  if (entry == accel_map_.end())
+  if (entry == accel_map_.end()) {
     return false;
-  if (controller_)
+  }
+  if (controller_) {
     return controller_->HandleAccelerator(entry->second);
+  }
   return false;
 }
 
@@ -244,8 +249,9 @@
 }
 
 OobeUI* WebUILoginView::GetOobeUI() {
-  if (!GetWebUI())
+  if (!GetWebUI()) {
     return nullptr;
+  }
 
   return static_cast<OobeUI*>(GetWebUI()->GetController());
 }
@@ -267,8 +273,9 @@
   DCHECK(web_view_);
   web_view_->SetBoundsRect(bounds());
 
-  for (auto& observer : observer_list_)
+  for (auto& observer : observer_list_) {
     observer.OnPositionRequiresUpdate();
+  }
 }
 
 void WebUILoginView::ChildPreferredSizeChanged(View* child) {
@@ -329,13 +336,15 @@
 bool WebUILoginView::TakeFocus(content::WebContents* source, bool reverse) {
   // In case of blocked UI (ex.: sign in is in progress)
   // we should not process focus change events.
-  if (!forward_keyboard_event_)
+  if (!forward_keyboard_event_) {
     return false;
+  }
 
   // FocusLoginShelf focuses either system tray or login shelf buttons.
   // Only do this if the login shelf is enabled.
-  if (shelf_enabled_)
+  if (shelf_enabled_) {
     LoginScreen::Get()->FocusLoginShelf(reverse);
+  }
   return shelf_enabled_;
 }
 
diff --git a/chrome/browser/ash/login/ui/webui_login_view.h b/chrome/browser/ui/ash/login/webui_login_view.h
similarity index 97%
rename from chrome/browser/ash/login/ui/webui_login_view.h
rename to chrome/browser/ui/ash/login/webui_login_view.h
index d178aab6..216c043 100644
--- a/chrome/browser/ash/login/ui/webui_login_view.h
+++ b/chrome/browser/ui/ash/login/webui_login_view.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 CHROME_BROWSER_ASH_LOGIN_UI_WEBUI_LOGIN_VIEW_H_
-#define CHROME_BROWSER_ASH_LOGIN_UI_WEBUI_LOGIN_VIEW_H_
+#ifndef CHROME_BROWSER_UI_ASH_LOGIN_WEBUI_LOGIN_VIEW_H_
+#define CHROME_BROWSER_UI_ASH_LOGIN_WEBUI_LOGIN_VIEW_H_
 
 #include <map>
 #include <string>
@@ -186,4 +186,4 @@
 
 }  // namespace ash
 
-#endif  // CHROME_BROWSER_ASH_LOGIN_UI_WEBUI_LOGIN_VIEW_H_
+#endif  // CHROME_BROWSER_UI_ASH_LOGIN_WEBUI_LOGIN_VIEW_H_
diff --git a/chrome/browser/ui/ash/main_extra_parts/BUILD.gn b/chrome/browser/ui/ash/main_extra_parts/BUILD.gn
index ac04985..435be9c 100644
--- a/chrome/browser/ui/ash/main_extra_parts/BUILD.gn
+++ b/chrome/browser/ui/ash/main_extra_parts/BUILD.gn
@@ -36,7 +36,6 @@
     "//chrome/browser/ash/input_device_settings",
     "//chrome/browser/ash/lobster",
     "//chrome/browser/ash/login/signin",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/magic_boost",
     "//chrome/browser/ash/mahi",
     "//chrome/browser/ash/mahi/media_app",
diff --git a/chrome/browser/ui/ash/main_extra_parts/DEPS b/chrome/browser/ui/ash/main_extra_parts/DEPS
index 97b79aa..b16be7bc 100644
--- a/chrome/browser/ui/ash/main_extra_parts/DEPS
+++ b/chrome/browser/ui/ash/main_extra_parts/DEPS
@@ -21,7 +21,6 @@
   "+chrome/browser/ash/input_device_settings",
   "+chrome/browser/ash/lobster",
   "+chrome/browser/ash/login/signin",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/magic_boost",
   "+chrome/browser/ash/mahi",
   "+chrome/browser/ash/net",
diff --git a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
index 42f3523..a3a9120 100644
--- a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
@@ -36,7 +36,6 @@
 #include "chrome/browser/ash/growth/campaigns_manager_session.h"
 #include "chrome/browser/ash/input_device_settings/peripherals_app_delegate_impl.h"
 #include "chrome/browser/ash/login/signin/signin_error_notifier_factory.h"
-#include "chrome/browser/ash/login/ui/oobe_dialog_util_impl.h"
 #include "chrome/browser/ash/magic_boost/magic_boost_state_ash.h"
 #include "chrome/browser/ash/mahi/mahi_manager_impl.h"
 #include "chrome/browser/ash/mahi/media_app/mahi_media_app_content_manager_impl.h"
@@ -68,6 +67,7 @@
 #include "chrome/browser/ui/ash/in_session_auth/in_session_auth_token_provider_impl.h"
 #include "chrome/browser/ui/ash/input_method/ime_controller_client_impl.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_util_impl.h"
 #include "chrome/browser/ui/ash/media_client/media_client_impl.h"
 #include "chrome/browser/ui/ash/network/mobile_data_notifications.h"
 #include "chrome/browser/ui/ash/network/network_connect_delegate.h"
diff --git a/chrome/browser/ui/ash/metrics/BUILD.gn b/chrome/browser/ui/ash/metrics/BUILD.gn
new file mode 100644
index 0000000..5921927
--- /dev/null
+++ b/chrome/browser/ui/ash/metrics/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+
+assert(is_chromeos_ash)
+
+source_set("browser_tests") {
+  testonly = true
+
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+  sources = [ "first_web_contents_profiler_ash_browsertest.cc" ]
+
+  deps = [
+    "//ash:test_support",
+    "//ash/constants",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser",
+    "//chrome/browser/ash/app_restore",
+    "//chrome/browser/ash/app_restore:test_support",
+    "//chrome/browser/ui",
+    "//chrome/test:test_support",
+    "//components/prefs",
+    "//content/public/browser",
+    "//content/test:test_support",
+  ]
+}
diff --git a/chrome/browser/ui/ash/metrics/DEPS b/chrome/browser/ui/ash/metrics/DEPS
new file mode 100644
index 0000000..9d75b2f
--- /dev/null
+++ b/chrome/browser/ui/ash/metrics/DEPS
@@ -0,0 +1,21 @@
+include_rules = [
+  # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for
+  # details.
+  "-chrome",
+
+  # This directory is in //chrome, which violates the rule above. Allow this
+  # directory to #include its own files.
+  "+chrome/browser/ui/ash/metrics",
+
+  # Existing dependencies within //chrome. There is an active effort to
+  # refactor ash codes in //chrome to break these dependencies; see b/332804822.
+  # Whenever possible, avoid adding new //chrome dependencies to this list.
+  "+chrome/browser/ash/app_restore",
+  "+chrome/browser/profiles/profile_manager.h",
+  "+chrome/browser/ui/browser.h",
+  "+chrome/browser/ui/browser_list.h",
+  "+chrome/browser/ui/browser_list_observer.h",
+  "+chrome/browser/ui/browser_tabstrip.h",
+  "+chrome/browser/ui/browser_window.h",
+  "+chrome/test",
+]
diff --git a/chrome/browser/ash/first_web_contents_profiler_ash_browsertest.cc b/chrome/browser/ui/ash/metrics/first_web_contents_profiler_ash_browsertest.cc
similarity index 100%
rename from chrome/browser/ash/first_web_contents_profiler_ash_browsertest.cc
rename to chrome/browser/ui/ash/metrics/first_web_contents_profiler_ash_browsertest.cc
diff --git a/chrome/browser/ui/ash/projector/BUILD.gn b/chrome/browser/ui/ash/projector/BUILD.gn
index b88a9c1..b545130 100644
--- a/chrome/browser/ui/ash/projector/BUILD.gn
+++ b/chrome/browser/ui/ash/projector/BUILD.gn
@@ -123,7 +123,6 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/system_web_apps",
     "//chrome/browser/ash/system_web_apps/test_support:test_support_ui",
@@ -131,6 +130,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
     "//chrome/browser/ui:browser_navigator_params_headers",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/test:test_support",
     "//chromeos/ash/components/drivefs:test_support",
diff --git a/chrome/browser/ui/ash/projector/DEPS b/chrome/browser/ui/ash/projector/DEPS
index e45c6c3..bee0842d 100644
--- a/chrome/browser/ui/ash/projector/DEPS
+++ b/chrome/browser/ui/ash/projector/DEPS
@@ -16,7 +16,6 @@
   "+chrome/browser/ash/extensions/file_manager",
   "+chrome/browser/ash/login/login_manager_test.h",
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/ash/system_web_apps",
@@ -29,6 +28,7 @@
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
   "+chrome/browser/speech",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/system_web_apps",
   "+chrome/browser/ui/browser_finder.h",
   "+chrome/browser/ui/browser.h",
diff --git a/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc b/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
index 8d46e4b..130959ca 100644
--- a/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
@@ -30,11 +30,11 @@
 #include "chrome/browser/ash/drive/drivefs_test_support.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/notifications/notification_display_service.h"
 #include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/projector/projector_app_client_impl.h"
 #include "chrome/browser/ui/ash/projector/projector_drivefs_provider.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/ui/ash/session/BUILD.gn b/chrome/browser/ui/ash/session/BUILD.gn
index 98b85e1..5a54f0e94 100644
--- a/chrome/browser/ui/ash/session/BUILD.gn
+++ b/chrome/browser/ui/ash/session/BUILD.gn
@@ -26,13 +26,13 @@
     "//chrome/browser/ash/floating_workspace",
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/lock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/off_hours",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/settings",
     "//chrome/browser/ash/system_web_apps/apps/personalization_app",
     "//chrome/browser/policy:onc",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/ash/shelf",
     "//chrome/common",
diff --git a/chrome/browser/ui/ash/session/DEPS b/chrome/browser/ui/ash/session/DEPS
index ff16ead..9adc75c 100644
--- a/chrome/browser/ui/ash/session/DEPS
+++ b/chrome/browser/ui/ash/session/DEPS
@@ -15,7 +15,6 @@
   "+chrome/browser/ash/floating_workspace",
   "+chrome/browser/ash/login/demo_mode",
   "+chrome/browser/ash/login/lock",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/policy/off_hours",
   "+chrome/browser/ash/profiles",
@@ -28,6 +27,7 @@
   "+chrome/browser/profiles",
   "+chrome/browser/supervised_user",
   "+chrome/browser/ui/ash/assistant",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/multi_user",
   "+chrome/browser/ui/browser_dialogs.h",
   "+chrome/browser/ui/managed_ui.h",
diff --git a/chrome/browser/ui/ash/session/session_controller_client_impl.cc b/chrome/browser/ui/ash/session/session_controller_client_impl.cc
index b3e1195..de5e88c3 100644
--- a/chrome/browser/ui/ash/session/session_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/session/session_controller_client_impl.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/ash/floating_workspace/floating_workspace_util.h"
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ash/login/lock/screen_locker.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_utils.h"
@@ -37,6 +36,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/managed_ui.h"
diff --git a/chrome/browser/ui/ash/system/BUILD.gn b/chrome/browser/ui/ash/system/BUILD.gn
index 3c4a2bfd..3bd03ea 100644
--- a/chrome/browser/ui/ash/system/BUILD.gn
+++ b/chrome/browser/ui/ash/system/BUILD.gn
@@ -26,7 +26,6 @@
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/profiles",
@@ -35,6 +34,7 @@
     "//chrome/browser/ash/system_web_apps/apps/personalization_app",
     "//chrome/browser/chromeos/extensions/vpn_provider",
     "//chrome/browser/ui:browser_navigator_params_headers",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/browser/ui/webui/ash/bluetooth",
     "//chrome/browser/ui/webui/ash/internet",
@@ -60,9 +60,9 @@
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/system_web_apps/apps/help_app",
+    "//chrome/browser/ui/ash/login",
   ]
 }
 
@@ -108,7 +108,6 @@
     "//chrome/browser/ash/login/lock:test_support",
     "//chrome/browser/ash/login/session:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/core:test_support",
     "//chrome/browser/ash/profiles",
@@ -117,6 +116,7 @@
     "//chrome/browser/media/router/discovery/access_code:access_code_cast_feature",
     "//chrome/browser/ui",
     "//chrome/browser/ui/ash/cast_config",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/web_applications",
     "//chrome/common",
     "//chrome/test/media_router/access_code_cast:access_code_cast_integration_base",
diff --git a/chrome/browser/ui/ash/system/DEPS b/chrome/browser/ui/ash/system/DEPS
index 2f31409b..361a0e2 100644
--- a/chrome/browser/ui/ash/system/DEPS
+++ b/chrome/browser/ui/ash/system/DEPS
@@ -19,7 +19,6 @@
   "+chrome/browser/ash/login/login_manager_test.h",
   "+chrome/browser/ash/login/session",
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/policy/core",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/ash/settings",
@@ -32,6 +31,7 @@
   "+chrome/browser/media/router/discovery/access_code",
   "+chrome/browser/profiles",
   "+chrome/browser/ui/ash/cast_config",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h",
   "+chrome/browser/ui/browser.h",
   "+chrome/browser/ui/browser_navigator_params.h",
diff --git a/chrome/browser/ui/ash/system/system_tray_client_impl_browsertest.cc b/chrome/browser/ui/ash/system/system_tray_client_impl_browsertest.cc
index 1981546..26647ee 100644
--- a/chrome/browser/ui/ash/system/system_tray_client_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/system/system_tray_client_impl_browsertest.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/ash/login/test/local_state_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_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"
@@ -36,6 +35,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/ui/ash/test/BUILD.gn b/chrome/browser/ui/ash/test/BUILD.gn
index 33fc626..a47a999 100644
--- a/chrome/browser/ui/ash/test/BUILD.gn
+++ b/chrome/browser/ui/ash/test/BUILD.gn
@@ -46,10 +46,10 @@
     "//chrome/browser",
     "//chrome/browser/ash/login:test_support",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui",
+    "//chrome/browser/ui/ash/login",
     "//chrome/common:non_code_constants",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
diff --git a/chrome/browser/ui/ash/test/DEPS b/chrome/browser/ui/ash/test/DEPS
index eee6b1a5..2957fe4f 100644
--- a/chrome/browser/ui/ash/test/DEPS
+++ b/chrome/browser/ui/ash/test/DEPS
@@ -12,9 +12,9 @@
   # Whenever possible, avoid adding new //chrome dependencies to this list.
   "+chrome/browser/apps/platform_apps",
   "+chrome/browser/ash/login",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/browser_commands.h",
   "+chrome/browser/ui/browser_finder.h",
   "+chrome/browser/ui/browser.h",
diff --git a/chrome/browser/ui/ash/test/feature_discovery_duration_reporter_browsertest.cc b/chrome/browser/ui/ash/test/feature_discovery_duration_reporter_browsertest.cc
index 2d45685..ede425d 100644
--- a/chrome/browser/ui/ash/test/feature_discovery_duration_reporter_browsertest.cc
+++ b/chrome/browser/ui/ash/test/feature_discovery_duration_reporter_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/public/cpp/feature_discovery_duration_reporter.h"
+
 #include "ash/public/cpp/feature_discovery_metric_util.h"
 #include "base/json/values_util.h"
 #include "base/metrics/histogram_base.h"
@@ -10,9 +11,9 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ui/ash/wallpaper/BUILD.gn b/chrome/browser/ui/ash/wallpaper/BUILD.gn
index c8fbdae8..6845c0b 100644
--- a/chrome/browser/ui/ash/wallpaper/BUILD.gn
+++ b/chrome/browser/ui/ash/wallpaper/BUILD.gn
@@ -29,7 +29,6 @@
     "//chrome/browser/ash/file_manager",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/lock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/external_data/handlers",
     "//chrome/browser/ash/system_web_apps/apps/personalization_app",
     "//chrome/browser/ash/wallpaper",
@@ -56,7 +55,6 @@
   allow_circular_includes_from = [
     "//chrome/browser/ash/arc/wallpaper",
     "//chrome/browser/ash/login/lock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/external_data/handlers",
     "//chrome/browser/ash/system_web_apps/apps/personalization_app",
     "//chrome/browser/ui/ash/login",
diff --git a/chrome/browser/ui/ash/wm/BUILD.gn b/chrome/browser/ui/ash/wm/BUILD.gn
index 8235f26..e80d4af7 100644
--- a/chrome/browser/ui/ash/wm/BUILD.gn
+++ b/chrome/browser/ui/ash/wm/BUILD.gn
@@ -31,13 +31,17 @@
 
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
-  sources = [ "snap_group_browsertest.cc" ]
+  sources = [
+    "snap_group_browsertest.cc",
+    "wmp_browsertest.cc",
+  ]
 
   deps = [
     "//ash",
     "//ash/constants",
     "//base",
     "//base/test:test_support",
+    "//chrome/browser/ash/app_restore",
     "//chrome/browser/ash/system_web_apps",
     "//chrome/browser/ui",
     "//chrome/browser/ui/ash/new_window",
diff --git a/chrome/browser/ui/ash/wm/DEPS b/chrome/browser/ui/ash/wm/DEPS
index c59e8ac..24923114d 100644
--- a/chrome/browser/ui/ash/wm/DEPS
+++ b/chrome/browser/ui/ash/wm/DEPS
@@ -10,16 +10,18 @@
   # Existing dependencies within //chrome. There is an active effort to
   # refactor ash codes in //chrome to break these dependencies; see b/332804822.
   # Whenever possible, avoid adding new //chrome dependencies to this list.
+  "+chrome/browser/ash/app_restore",
   "+chrome/browser/ash/system_web_apps",
   "+chrome/browser/profiles/profile_manager.h",
   "+chrome/browser/ui/ash/new_window",
   "+chrome/browser/ui/ash/system_web_apps",
-  "+chrome/browser/ui/browser_finder.h",
   "+chrome/browser/ui/browser.h",
+  "+chrome/browser/ui/browser_finder.h",
   "+chrome/browser/ui/browser_list.h",
   "+chrome/browser/ui/browser_tabstrip.h",
   "+chrome/browser/ui/browser_tab_strip_tracker.h",
   "+chrome/browser/ui/browser_window.h",
+  "+chrome/browser/ui/settings_window_manager_chromeos.h",
   "+chrome/browser/ui/tabs",
   "+chrome/browser/ui/views/frame",
   "+chrome/browser/ui/views/tabs",
diff --git a/chrome/browser/ash/wmp_browsertest.cc b/chrome/browser/ui/ash/wm/wmp_browsertest.cc
similarity index 100%
rename from chrome/browser/ash/wmp_browsertest.cc
rename to chrome/browser/ui/ash/wm/wmp_browsertest.cc
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
index 8290647..bcecc73 100644
--- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
+++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager.cc
@@ -159,14 +159,12 @@
 }
 
 void DesktopPaymentsWindowManager::OnDidFinishNavigationForVcn3ds() {
-  base::expected<RedirectCompletionResult,
-                 Vcn3dsAuthenticationPopupNonSuccessResult>
-      result = ParseUrlForVcn3ds(
+  base::expected<RedirectCompletionResult, Vcn3dsAuthenticationResult> result =
+      ParseUrlForVcn3ds(
           web_contents()->GetVisibleURL(),
           vcn_3ds_context_->challenge_option.vcn_3ds_metadata.value());
   if (result.has_value() ||
-      result.error() ==
-          Vcn3dsAuthenticationPopupNonSuccessResult::kAuthenticationFailed) {
+      result.error() == Vcn3dsAuthenticationResult::kAuthenticationFailed) {
     // To safely close the pop-up during a navigation event, a task must be
     // posted to the current base::SequencedTaskRunner, as the web contents must
     // complete notifying all of its observers of the navigation event before
@@ -179,9 +177,8 @@
 
 void DesktopPaymentsWindowManager::OnWebContentsDestroyedForVcn3ds() {
   CHECK(vcn_3ds_popup_shown_timestamp_.has_value());
-  base::expected<RedirectCompletionResult,
-                 Vcn3dsAuthenticationPopupNonSuccessResult>
-      result = ParseUrlForVcn3ds(
+  base::expected<RedirectCompletionResult, Vcn3dsAuthenticationResult> result =
+      ParseUrlForVcn3ds(
           web_contents()->GetVisibleURL(),
           vcn_3ds_context_->challenge_option.vcn_3ds_metadata.value());
 
@@ -210,8 +207,7 @@
   // introduced invalid query parameters on the last redirect, this would fail
   // to handle that correctly, but it is not feasible to distinguish that from
   // the user closing the pop-up.
-  if (result.error() ==
-      Vcn3dsAuthenticationPopupNonSuccessResult::kAuthenticationFailed) {
+  if (result.error() == Vcn3dsAuthenticationResult::kAuthenticationFailed) {
     autofill_metrics::LogVcn3dsFlowEvent(
         Vcn3dsFlowEvent::kAuthenticationInsidePopupFailed,
         /*user_consent_already_given=*/vcn_3ds_context_
@@ -234,8 +230,9 @@
   // notified of the flow's completion.
   // TODO(crbug.com/334967738): Check whether the user closed the pop-up window
   // directly once an API for it is built.
-  std::move(vcn_3ds_context_->completion_callback)
-      .Run(Vcn3dsAuthenticationResponse());
+  Vcn3dsAuthenticationResponse response;
+  response.result = result.error();
+  std::move(vcn_3ds_context_->completion_callback).Run(std::move(response));
   Reset();
 }
 
@@ -256,8 +253,9 @@
 void DesktopPaymentsWindowManager::OnVcn3dsAuthenticationResponseReceived(
     PaymentsAutofillClient::PaymentsRpcResult result,
     const PaymentsNetworkInterface::UnmaskResponseDetails& response_details) {
-  Vcn3dsAuthenticationResponse response = CreateVcn3dsAuthenticationResponse(
-      result, response_details, std::move(vcn_3ds_context_->card));
+  Vcn3dsAuthenticationResponse response =
+      CreateVcn3dsAuthenticationResponseFromServerResult(
+          result, response_details, std::move(vcn_3ds_context_->card));
   client_->GetPaymentsAutofillClient()->CloseAutofillProgressDialog(
       /*show_confirmation_before_closing=*/response.card.has_value(),
       /*no_interactive_authentication_callback=*/base::OnceClosure());
@@ -290,8 +288,9 @@
       ->CancelRequest();
   // In the case of the dialog cancelled, we still run the callback to let the
   // caller know the flow has finished unsuccessfully.
-  std::move(vcn_3ds_context_->completion_callback)
-      .Run(Vcn3dsAuthenticationResponse());
+  Vcn3dsAuthenticationResponse response;
+  response.result = Vcn3dsAuthenticationResult::kAuthenticationNotCompleted;
+  std::move(vcn_3ds_context_->completion_callback).Run(std::move(response));
   Reset();
 }
 
@@ -326,8 +325,9 @@
           ->user_consent_already_given);
   // In the case of the dialog cancelled, we still run the callback to let the
   // caller know the flow has finished unsuccessfully.
-  std::move(vcn_3ds_context_->completion_callback)
-      .Run(Vcn3dsAuthenticationResponse());
+  Vcn3dsAuthenticationResponse response;
+  response.result = Vcn3dsAuthenticationResult::kAuthenticationNotCompleted;
+  std::move(vcn_3ds_context_->completion_callback).Run(std::move(response));
   Reset();
 }
 
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
index b06d012..fa3a3da 100644
--- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
+++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
@@ -491,6 +491,9 @@
       authentication_response();
   ASSERT_TRUE(response.has_value());
   EXPECT_FALSE(response->card.has_value());
+  EXPECT_EQ(
+      response->result,
+      PaymentsWindowManager::Vcn3dsAuthenticationResult::kAuthenticationFailed);
 }
 
 // Tests that the VCN 3DS flow failed during second server call histogram bucket
@@ -560,6 +563,9 @@
       authentication_response();
   ASSERT_TRUE(response.has_value());
   EXPECT_FALSE(response->card.has_value());
+  EXPECT_EQ(
+      response->result,
+      PaymentsWindowManager::Vcn3dsAuthenticationResult::kAuthenticationFailed);
   EXPECT_TRUE(
       client()->GetPaymentsAutofillClient()->autofill_error_dialog_shown());
 }
@@ -614,6 +620,9 @@
       authentication_response();
   ASSERT_TRUE(response.has_value());
   EXPECT_FALSE(response->card.has_value());
+  EXPECT_EQ(response->result,
+            PaymentsWindowManager::Vcn3dsAuthenticationResult::
+                kAuthenticationNotCompleted);
   EXPECT_FALSE(
       client()->GetPaymentsAutofillClient()->autofill_error_dialog_shown());
 }
@@ -667,6 +676,9 @@
       authentication_response();
   ASSERT_TRUE(response.has_value());
   EXPECT_FALSE(response->card.has_value());
+  EXPECT_EQ(response->result,
+            PaymentsWindowManager::Vcn3dsAuthenticationResult::
+                kAuthenticationNotCompleted);
   EXPECT_FALSE(
       client()->GetPaymentsAutofillClient()->autofill_error_dialog_shown());
 }
@@ -703,6 +715,9 @@
       authentication_response();
   ASSERT_TRUE(response.has_value());
   EXPECT_FALSE(response->card.has_value());
+  EXPECT_EQ(response->result,
+            PaymentsWindowManager::Vcn3dsAuthenticationResult::
+                kAuthenticationNotCompleted);
   EXPECT_TRUE(test_api(window_manager()).NoOngoingFlow());
 }
 
diff --git a/chrome/browser/ui/browser_actions.cc b/chrome/browser/ui/browser_actions.cc
index ecb6e27..d65691f1 100644
--- a/chrome/browser/ui/browser_actions.cc
+++ b/chrome/browser/ui/browser_actions.cc
@@ -458,6 +458,7 @@
                 [](Browser* browser, actions::ActionItem* item,
                    actions::ActionInvocationContext context) {
                   chrome::CopyURL(
+                      browser,
                       browser->tab_strip_model()->GetActiveWebContents());
                 },
                 base::Unretained(browser)),
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 8c133ab..7fcb036 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1002,7 +1002,7 @@
       break;
     // Hosted App commands
     case IDC_COPY_URL:
-      CopyURL(browser_->tab_strip_model()->GetActiveWebContents());
+      CopyURL(browser_, browser_->tab_strip_model()->GetActiveWebContents());
       break;
     case IDC_OPEN_IN_CHROME:
       OpenInChrome(browser_);
@@ -2006,8 +2006,7 @@
   customize_chrome::SidePanelController* side_panel_controller =
       tab->tab_features()->customize_chrome_side_panel_controller();
 
-  if (!side_panel_controller ||
-      !side_panel_controller->IsCustomizeChromeEntryAvailable()) {
+  if (!side_panel_controller) {
     return;
   }
 
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 6f1e827..e348c677 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -1486,6 +1486,13 @@
       browser->bookmark_bar_state());
 #if !BUILDFLAG(IS_ANDROID)
   if (toast_features::IsEnabled(toast_features::kReadingListToast)) {
+    // Don't show the reading list toast if the side panel is visible.
+    std::optional<SidePanelEntry::Id> id =
+        browser->GetFeatures().side_panel_ui()->GetCurrentEntryId();
+    if (id.has_value() && id.value() == SidePanelEntryId::kReadingList) {
+      return true;
+    }
+
     ToastController* const toast_controller =
         browser->GetFeatures().toast_controller();
     if (toast_controller) {
@@ -2118,9 +2125,19 @@
                   : false;
 }
 
-void CopyURL(content::WebContents* web_contents) {
+void CopyURL(Browser* browser, content::WebContents* web_contents) {
   ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
   scw.WriteText(base::UTF8ToUTF16(web_contents->GetVisibleURL().spec()));
+
+#if !BUILDFLAG(IS_ANDROID)
+  if (toast_features::IsEnabled(toast_features::kLinkCopiedToast)) {
+    ToastController* const toast_controller =
+        browser->GetFeatures().toast_controller();
+    if (toast_controller) {
+      toast_controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied));
+    }
+  }
+#endif
 }
 
 bool CanCopyUrl(const Browser* browser) {
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h
index 1ce59e9d..71f4ae23 100644
--- a/chrome/browser/ui/browser_commands.h
+++ b/chrome/browser/ui/browser_commands.h
@@ -244,7 +244,7 @@
 void ToggleFullscreenMode(Browser* browser);
 void ClearCache(Browser* browser);
 bool IsDebuggerAttachedToCurrentTab(Browser* browser);
-void CopyURL(content::WebContents* web_contents);
+void CopyURL(Browser* browser, content::WebContents* web_contents);
 bool CanCopyUrl(const Browser* browser);
 // Returns true if the browser window is for a web app or custom tab.
 bool IsWebAppOrCustomTab(const Browser* browser);
diff --git a/chrome/browser/ui/browser_commands_browsertest.cc b/chrome/browser/ui/browser_commands_browsertest.cc
index 750b528..e846908 100644
--- a/chrome/browser/ui/browser_commands_browsertest.cc
+++ b/chrome/browser/ui/browser_commands_browsertest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 
 #include "base/path_service.h"
+#include "base/task/current_thread.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -22,6 +23,8 @@
 #include "chrome/browser/ui/toasts/toast_controller.h"
 #include "chrome/browser/ui/toasts/toast_features.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
+#include "chrome/browser/ui/views/side_panel/side_panel_entry_id.h"
 #include "chrome/browser/ui/webui/commerce/product_specifications_disclosure_dialog.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -47,6 +50,7 @@
             features::kTabOrganization,
             toast_features::kToastFramework,
             toast_features::kReadingListToast,
+            toast_features::kLinkCopiedToast,
         },
         {});
   }
@@ -424,4 +428,24 @@
   EXPECT_TRUE(browser()->GetFeatures().toast_controller()->IsShowingToast());
 }
 
+IN_PROC_BROWSER_TEST_F(BrowserCommandsTest,
+                       AddingToReadingListWithSidePanelShowsNoToast) {
+  GURL main_url(https_server_.GetURL("a.test", "/iframe.html"));
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
+  auto* side_panel_coordinator =
+      browser()->GetFeatures().side_panel_coordinator();
+  side_panel_coordinator->Show(SidePanelEntryId::kReadingList);
+  ASSERT_TRUE(base::test::RunUntil(
+      [&]() { return side_panel_coordinator->IsSidePanelShowing(); }));
+  chrome::ExecuteCommand(browser(), IDC_READING_LIST_MENU_ADD_TAB);
+  EXPECT_FALSE(browser()->GetFeatures().toast_controller()->IsShowingToast());
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserCommandsTest, CopyingUrlOpensToast) {
+  GURL main_url(https_server_.GetURL("a.test", "/iframe.html"));
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
+  chrome::ExecuteCommand(browser(), IDC_COPY_URL);
+  EXPECT_TRUE(browser()->GetFeatures().toast_controller()->IsShowingToast());
+}
+
 }  // namespace chrome
diff --git a/chrome/browser/ui/browser_element_identifiers.cc b/chrome/browser/ui/browser_element_identifiers.cc
index da40fdf2..3c1c726 100644
--- a/chrome/browser/ui/browser_element_identifiers.cc
+++ b/chrome/browser/ui/browser_element_identifiers.cc
@@ -90,7 +90,8 @@
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleSaveToggleId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabGroupHeaderElementId);
-DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabOrganizationButtonElementId);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kAutoTabGroupButtonElementId);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabDeclutterButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabSearchBubbleElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabSearchButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabStripElementId);
diff --git a/chrome/browser/ui/browser_element_identifiers.h b/chrome/browser/ui/browser_element_identifiers.h
index c6cd94f..584408b2 100644
--- a/chrome/browser/ui/browser_element_identifiers.h
+++ b/chrome/browser/ui/browser_element_identifiers.h
@@ -102,7 +102,8 @@
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabGroupEditorBubbleSaveToggleId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabGroupHeaderElementId);
-DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabOrganizationButtonElementId);
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kAutoTabGroupButtonElementId);
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabDeclutterButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabSearchBubbleElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabSearchButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabStripElementId);
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index b9cadf4ec..e9d28fe 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -608,10 +608,22 @@
     return nullptr;
   }
 
-  if (source_browser &&
-      platform_util::IsBrowserLockedFullscreen(source_browser)) {
-    // Block any navigation requests in locked fullscreen mode.
-    return nullptr;
+  // Block navigation requests when in locked fullscreen mode. We allow
+  // navigation requests in the webapp when locked for OnTask (only relevant for
+  // non-web browser scenarios).
+  // TODO(b/365146870): Remove once we consolidate locked fullscreen with
+  // OnTask.
+  if (source_browser) {
+    bool should_block_navigation =
+        platform_util::IsBrowserLockedFullscreen(source_browser);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    if (source_browser->IsLockedForOnTask()) {
+      should_block_navigation = false;
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+    if (should_block_navigation) {
+      return nullptr;
+    }
   }
 
   // Open System Apps in their standalone window if necessary.
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index 535f627a..7915e9d 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
+#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_tabstrip.h"
@@ -52,6 +53,7 @@
 #include "content/public/test/test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
 #include "net/test/scoped_mutually_exclusive_feature_list.h"
 #include "services/network/public/cpp/resource_request_body.h"
 #include "third_party/blink/public/common/features.h"
@@ -2306,6 +2308,97 @@
           .ExtractBool());
 }
 
+// Test that a popin cannot navigate to an HTTP page
+IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, PopinHttpNavigation) {
+  // Setup browser.
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+  https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+  ASSERT_TRUE(https_server.Start());
+  net::EmbeddedTestServer http_server(net::EmbeddedTestServer::TYPE_HTTP);
+  http_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+  ASSERT_TRUE(http_server.Start());
+  const GURL secure_url = https_server.GetURL("a.test", "/empty.html");
+  const GURL insecure_url = http_server.GetURL("a.test", "/empty.html");
+  content::WebContents* tab_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), secure_url));
+
+  // Open popin and verify it's visible.
+  content::WebContentsAddedObserver new_tab_observer;
+  EXPECT_TRUE(content::ExecJs(
+      tab_web_contents,
+      "window.open('" + secure_url.spec() + "', '_blank', 'popin')"));
+  content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
+  BrowserWindow* popin_browser_window =
+      BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents);
+  EXPECT_NE(popin_browser_window, browser()->window());
+  EXPECT_TRUE(popin_browser_window->IsVisible());
+
+  // Navigating to HTTP page fails.
+  EXPECT_TRUE(content::ExecJs(
+      popin_web_contents, "window.location = '" + insecure_url.spec() + "';"));
+  EXPECT_EQ("about:blank",
+            content::EvalJs(popin_web_contents, "window.location.href")
+                .ExtractString());
+}
+
+std::unique_ptr<net::test_server::HttpResponse> HandlePopinServerRedirect(
+    const net::test_server::HttpRequest& request) {
+  const GURL& url = request.GetURL();
+  net::test_server::RequestQuery query = net::test_server::ParseQuery(url);
+  if (query.find("redirect") == query.end()) {
+    return nullptr;
+  }
+  std::string destination = query["redirect"][0];
+  auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+  response->AddCustomHeader("Location", destination);
+  response->set_code(net::HTTP_FOUND);
+  response->set_content_type("text/html");
+  response->set_content(base::StringPrintf(
+      "<html><head></head><body>Redirecting to %s</body></html>",
+      destination.c_str()));
+  return response;
+}
+
+// Test that a popin cannot navigate to an HTTP page
+IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest, PopinHttpRedirectNavigation) {
+  // Setup browser.
+  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+  https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+  https_server.RegisterRequestHandler(
+      base::BindRepeating(&HandlePopinServerRedirect));
+  ASSERT_TRUE(https_server.Start());
+  net::EmbeddedTestServer http_server(net::EmbeddedTestServer::TYPE_HTTP);
+  http_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+  ASSERT_TRUE(http_server.Start());
+  const GURL secure_url = https_server.GetURL("a.test", "/empty.html");
+  const GURL insecure_url = http_server.GetURL("a.test", "/empty.html");
+  content::WebContents* tab_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), secure_url));
+
+  // Open popin and verify it's visible.
+  content::WebContentsAddedObserver new_tab_observer;
+  EXPECT_TRUE(content::ExecJs(
+      tab_web_contents,
+      "window.open('" + secure_url.spec() + "', '_blank', 'popin')"));
+  content::WebContents* popin_web_contents = new_tab_observer.GetWebContents();
+  BrowserWindow* popin_browser_window =
+      BrowserWindow::FindBrowserWindowWithWebContents(popin_web_contents);
+  EXPECT_NE(popin_browser_window, browser()->window());
+  EXPECT_TRUE(popin_browser_window->IsVisible());
+
+  // Navigating to HTTP page fails.
+  EXPECT_TRUE(content::ExecJs(popin_web_contents,
+                              "window.location = '" + secure_url.spec() +
+                                  "?redirect=" + insecure_url.spec() + "';"));
+  EXPECT_EQ("about:blank",
+            content::EvalJs(popin_web_contents, "window.location.href")
+                .ExtractString());
+}
+
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 // This class extends the basic logic in display::ScreenBase to allow us to mock
 // the call to `GetDisplayNearestWindow`. This provides a way to ensure that the
diff --git a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
index 790263a..5cc5aa7 100644
--- a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
@@ -97,6 +97,37 @@
       params.browser->tab_strip_model()->GetActiveWebContents()->GetURL());
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Verify that page navigation is allowed in locked fullscreen mode when locked
+// for OnTask. Only applicable for non-web browser scenarios.
+IN_PROC_BROWSER_TEST_F(BrowserNavigatorTestChromeOS,
+                       NavigationAllowedInLockedFullscreenWhenLockedForOnTask) {
+  // Set locked fullscreen state.
+  aura::Window* const window = browser()->window()->GetNativeWindow();
+  PinWindow(window, /*trusted=*/true);
+  browser()->SetLockedForOnTask(true);
+
+  // Navigate to a page.
+  const GURL kUrl(chrome::kChromeUIVersionURL);
+  NavigateParams params(MakeNavigateParams(browser()));
+  params.disposition = WindowOpenDisposition::NEW_WINDOW;
+  params.url = kUrl;
+  params.window_action = NavigateParams::SHOW_WINDOW;
+  Navigate(&params);
+
+  // The original browser should still be at the same page, but the newly
+  // opened browser should sit on the chrome:version page.
+  ASSERT_EQ(2u, chrome::GetTotalBrowserCount());
+  ASSERT_EQ(1, browser()->tab_strip_model()->count());
+  EXPECT_EQ(GURL(url::kAboutBlankURL),
+            browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
+  ASSERT_EQ(1, params.browser->tab_strip_model()->count());
+  EXPECT_EQ(
+      kUrl,
+      params.browser->tab_strip_model()->GetActiveWebContents()->GetURL());
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 // Subclass that tests navigation while in the Guest session.
 class BrowserGuestSessionNavigatorTest : public BrowserNavigatorTest {
  protected:
diff --git a/chrome/browser/ui/browser_tab_strip_model_delegate.cc b/chrome/browser/ui/browser_tab_strip_model_delegate.cc
index 331bc3e..e5a0118 100644
--- a/chrome/browser/ui/browser_tab_strip_model_delegate.cc
+++ b/chrome/browser/ui/browser_tab_strip_model_delegate.cc
@@ -308,7 +308,7 @@
 }
 
 void BrowserTabStripModelDelegate::CopyURL(content::WebContents* web_contents) {
-  chrome::CopyURL(web_contents);
+  chrome::CopyURL(browser_, web_contents);
 }
 
 void BrowserTabStripModelDelegate::GoBack(content::WebContents* web_contents) {
diff --git a/chrome/browser/ui/chromeos/BUILD.gn b/chrome/browser/ui/chromeos/BUILD.gn
index c2cac983..7f96e2c 100644
--- a/chrome/browser/ui/chromeos/BUILD.gn
+++ b/chrome/browser/ui/chromeos/BUILD.gn
@@ -26,6 +26,7 @@
     "//chrome/browser",
     "//chrome/browser/ui",
     "//chrome/browser/ui/exclusive_access",
+    "//chrome/browser/ui/exclusive_access:test_support",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//chromeos/ui/base",
diff --git a/chrome/browser/ui/exclusive_access/BUILD.gn b/chrome/browser/ui/exclusive_access/BUILD.gn
index 1782aeb..df3905a8 100644
--- a/chrome/browser/ui/exclusive_access/BUILD.gn
+++ b/chrome/browser/ui/exclusive_access/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/args.gni")
+import("//build/config/ozone.gni")
 import("//extensions/buildflags/buildflags.gni")
 
 assert(is_win || is_mac || is_linux || is_chromeos)
@@ -71,3 +73,71 @@
     ]
   }
 }
+
+if (!is_android) {
+  source_set("test_support") {
+    testonly = true
+    sources = [
+      "exclusive_access_test.cc",
+      "exclusive_access_test.h",
+      "fullscreen_controller_state_test.cc",
+      "fullscreen_controller_state_test.h",
+      "fullscreen_controller_state_tests.h",
+    ]
+    public_deps = [
+      ":exclusive_access",
+      "//chrome/test:test_support",
+      "//chrome/test:test_support_ui",
+      "//content/test:test_support",
+    ]
+  }
+
+  source_set("unit_tests") {
+    testonly = true
+    sources = [
+      "exclusive_access_bubble_unittest.cc",
+      "exclusive_access_permission_manager_unittest.cc",
+      "fullscreen_controller_state_unittest.cc",
+    ]
+    deps = [ ":test_support" ]
+  }
+
+  source_set("browser_tests") {
+    testonly = true
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    sources = [
+      "exclusive_access_manager_browsertest.cc",
+      "fullscreen_controller_browsertest.cc",
+      "pointer_lock_controller_browsertest.cc",
+    ]
+
+    deps = [
+      ":test_support",
+      "//chrome/browser/ui:ui_features",
+      "//components/content_settings/core/browser",
+    ]
+  }
+
+  if (!is_chromeos_device) {
+    source_set("interactive_ui_tests") {
+      testonly = true
+      defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+      sources = [
+        "fullscreen_controller_interactive_browsertest.cc",
+        "fullscreen_controller_state_interactive_browsertest.cc",
+        "fullscreen_interactive_browsertest.cc",
+      ]
+      deps = [
+        ":test_support",
+        "//chrome/browser/ui:test_support",
+        "//chrome/browser/web_applications:web_applications_test_support",
+        "//components/blocked_content",
+        "//components/metrics:content",
+        "//ui/display:test_support",
+      ]
+      if (use_ozone) {
+        deps += [ "//ui/ozone" ]
+      }
+    }
+  }
+}
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_interactive_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_interactive_browsertest.cc
index 4d15d98..ade5836 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_interactive_browsertest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_interactive_browsertest.cc
@@ -7,7 +7,6 @@
 #include "base/test/run_until.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/test/fullscreen_test_util.h"
 #include "chrome/browser/ui/test/popup_test_base.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/ui/lens/BUILD.gn b/chrome/browser/ui/lens/BUILD.gn
index e6577b7..b7200e0 100644
--- a/chrome/browser/ui/lens/BUILD.gn
+++ b/chrome/browser/ui/lens/BUILD.gn
@@ -16,8 +16,8 @@
     "lens_overlay_entry_point_controller.h",
     "lens_overlay_invocation_source.h",
     "lens_overlay_side_panel_navigation_throttle.h",
-    "lens_untrusted_ui.h",
-    "lens_untrusted_ui_config.h",
+    "lens_overlay_untrusted_ui.h",
+    "lens_side_panel_untrusted_ui.h",
     "search_bubble_ui.h",
   ]
   friend = [ ":*" ]
@@ -47,6 +47,7 @@
     "lens_overlay_side_panel_web_view.h",
     "lens_overlay_theme_utils.cc",
     "lens_overlay_theme_utils.h",
+    "lens_overlay_untrusted_ui.cc",
     "lens_overlay_url_builder.cc",
     "lens_overlay_url_builder.h",
     "lens_permission_bubble_controller.cc",
@@ -55,8 +56,7 @@
     "lens_preselection_bubble.h",
     "lens_search_bubble_controller.cc",
     "lens_search_bubble_controller.h",
-    "lens_untrusted_ui.cc",
-    "lens_untrusted_ui_config.cc",
+    "lens_side_panel_untrusted_ui.cc",
     "ref_counted_lens_overlay_client_logs.h",
     "search_bubble_page_handler.cc",
     "search_bubble_page_handler.h",
diff --git a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc
index 91ed8196..575dc837 100644
--- a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc
+++ b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc
@@ -89,6 +89,7 @@
 void LensOverlayBlurLayerDelegate::RenderWidgetHostDestroyed(
     content::RenderWidgetHost* widget_host) {
   CHECK(widget_host == background_view_host_);
+  render_widget_host_observer_.Reset();
   background_view_host_ = nullptr;
   // If the host view was destroyed, stop updating the background blur.
   screenshot_timer_.Stop();
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index 150c932..2d5ce5e5 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -1588,8 +1588,7 @@
   web_view->GetWebContents()->SetDelegate(this);
 
   // Load the untrusted WebUI into the web view.
-  GURL url(chrome::kChromeUILensUntrustedURL);
-  web_view->LoadInitialURL(url);
+  web_view->LoadInitialURL(GURL(chrome::kChromeUILensOverlayUntrustedURL));
 
   overlay_web_view_ = host_view->AddChildView(std::move(web_view));
   return host_view;
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
index 82032fc..401ef24 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -968,9 +968,9 @@
       [&]() { return controller->state() == State::kOverlay; }));
 
   // Assert that the web view was created and loaded WebUI.
-  GURL webui_url(chrome::kChromeUILensUntrustedURL);
   ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
-  ASSERT_EQ(GetOverlayWebContents()->GetLastCommittedURL(), webui_url);
+  ASSERT_EQ(GetOverlayWebContents()->GetLastCommittedURL(),
+            GURL(chrome::kChromeUILensOverlayUntrustedURL));
 }
 
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, ShowSidePanel) {
@@ -2482,6 +2482,8 @@
   ASSERT_TRUE(base::test::RunUntil(
       [&]() { return controller->state() == State::kOverlayAndResults; }));
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
+  EXPECT_TRUE(content::WaitForLoadStop(
+      controller->GetSidePanelWebContentsForTesting()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
   auto* fake_query_controller = static_cast<LensOverlayQueryControllerFake*>(
       controller->get_lens_overlay_query_controller_for_testing());
@@ -2493,9 +2495,10 @@
       "https://www.google.com/"
       "search?source=chrome.cr.ctxi&q=&lns_fp=1&lns_mode=un"
       "&gsc=2&hl=en-US&cs=0");
+  content::TestNavigationObserver first_observer(
+      controller->GetSidePanelWebContentsForTesting());
   controller->LoadURLInResultsFrame(first_search_url);
-  EXPECT_TRUE(content::WaitForLoadStop(
-      controller->GetSidePanelWebContentsForTesting()));
+  first_observer.WaitForNavigationFinished();
 
   // The search query history stack should be empty and the currently loaded
   // query should be set.
@@ -2519,12 +2522,10 @@
       "https://www.google.com/"
       "search?source=chrome.cr.ctxi&vsint=KgwKAggHEgIIAxgBIAI&q=kiwi&lns_fp=1&"
       "lns_mode=text&gsc=2&hl=en-US&cs=0");
-  // We can't use content::WaitForLoadStop here since the last navigation is
-  // successful.
-  content::TestNavigationObserver observer(
+  content::TestNavigationObserver second_observer(
       controller->GetSidePanelWebContentsForTesting());
   controller->IssueTextSelectionRequestForTesting("kiwi", 1, 100);
-  observer.WaitForNavigationFinished();
+  second_observer.WaitForNavigationFinished();
 
   // The search query history stack should have 1 entry and the currently loaded
   // query should be set to the new query
@@ -2543,7 +2544,7 @@
   EXPECT_EQ(loaded_search_query->lens_selection_type_,
             lens::SELECT_TEXT_HIGHLIGHT);
   GURL url_without_start_time_or_size =
-      RemoveStartTimeAndSizeParams(observer.last_navigation_url());
+      RemoveStartTimeAndSizeParams(second_observer.last_navigation_url());
   EXPECT_EQ(url_without_start_time_or_size, second_search_url);
 
   // Popping a query with a region should resend a region search request.
@@ -2562,10 +2563,10 @@
 
   // The full sequence of events necessary to load Lens search results is not
   // currently testable, so load the expected URL manually.
-  content::TestNavigationObserver pop_observer(
+  content::TestNavigationObserver third_observer(
       controller->GetSidePanelWebContentsForTesting());
   controller->LoadURLInResultsFrame(first_search_url);
-  pop_observer.Wait();
+  third_observer.WaitForNavigationFinished();
 
   // The search query history stack should be empty and the currently loaded
   // query should be set to the original query.
@@ -2584,8 +2585,8 @@
   EXPECT_EQ(loaded_search_query->selected_region_bitmap_.height(), 100);
   EXPECT_FALSE(loaded_search_query->selected_text_);
   EXPECT_EQ(loaded_search_query->lens_selection_type_, lens::INJECTED_IMAGE);
-  VerifySearchQueryParameters(pop_observer.last_navigation_url());
-  VerifyTextQueriesAreEqual(pop_observer.last_navigation_url(),
+  VerifySearchQueryParameters(third_observer.last_navigation_url());
+  VerifyTextQueriesAreEqual(third_observer.last_navigation_url(),
                             first_search_url);
 }
 
@@ -2610,6 +2611,8 @@
   ASSERT_TRUE(base::test::RunUntil(
       [&]() { return controller->state() == State::kOverlayAndResults; }));
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
+  EXPECT_TRUE(content::WaitForLoadStop(
+      controller->GetSidePanelWebContentsForTesting()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
   // Loading a url in the side panel should show the results page.
@@ -2617,9 +2620,10 @@
       "https://www.google.com/"
       "search?source=chrome.cr.ctxi&q=&lns_fp=1&lns_mode=un"
       "&gsc=2&hl=en-US&cs=0");
+  content::TestNavigationObserver first_load_observer(
+      controller->GetSidePanelWebContentsForTesting());
   controller->LoadURLInResultsFrame(first_search_url);
-  EXPECT_TRUE(content::WaitForLoadStop(
-      controller->GetSidePanelWebContentsForTesting()));
+  first_load_observer.WaitForNavigationFinished();
 
   // The search query history stack should be empty and the currently loaded
   // query should be set.
@@ -3585,7 +3589,7 @@
   EXPECT_FALSE(
       lens::LensOverlayControllerGlue::FromWebContents(active_contents));
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), GURL(chrome::kChromeUILensUntrustedURL)));
+      browser(), GURL(chrome::kChromeUILensOverlayUntrustedURL)));
 }
 
 class LensOverlayControllerBrowserFullscreenDisabled
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_interactive_uitest.cc b/chrome/browser/ui/lens/lens_overlay_controller_interactive_uitest.cc
index e1bcba88..083e04a3 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_interactive_uitest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/lens/lens_overlay_invocation_source.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/search_test_utils.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
 #include "chrome/test/user_education/interactive_feature_promo_test.h"
@@ -363,10 +364,11 @@
       // The overlay controller is an independent floating widget associated
       // with a tab rather than a browser window, so by convention gets its own
       // element context.
-      InAnyContext(Steps(InstrumentNonTabWebView(
-                             kOverlayId, LensOverlayController::kOverlayId),
-                         WaitForWebContentsReady(
-                             kOverlayId, GURL("chrome-untrusted://lens")))),
+      InAnyContext(Steps(
+          InstrumentNonTabWebView(kOverlayId,
+                                  LensOverlayController::kOverlayId),
+          WaitForWebContentsReady(
+              kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
       // Wait for the webview to finish loading to prevent re-entrancy.
       InSameContext(Steps(EnsurePresent(kOverlayId, kPathToCloseButton),
                           ExecuteJsAt(kOverlayId, kPathToCloseButton, kClickFn,
@@ -406,10 +408,11 @@
       // The overlay controller is an independent floating widget associated
       // with a tab rather than a browser window, so by convention gets its own
       // element context.
-      InAnyContext(Steps(InstrumentNonTabWebView(
-                             kOverlayId, LensOverlayController::kOverlayId),
-                         WaitForWebContentsReady(
-                             kOverlayId, GURL("chrome-untrusted://lens")))),
+      InAnyContext(Steps(
+          InstrumentNonTabWebView(kOverlayId,
+                                  LensOverlayController::kOverlayId),
+          WaitForWebContentsReady(
+              kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
       // Wait for the webview to finish loading to prevent re-entrancy.
       InSameContext(Steps(SendAccelerator(kOverlayId, escape_key),
                           WaitForHide(kOverlayId))));
@@ -453,10 +456,11 @@
       // The overlay controller is an independent floating widget associated
       // with a tab rather than a browser window, so by convention gets its own
       // element context.
-      InAnyContext(Steps(InstrumentNonTabWebView(
-                             kOverlayId, LensOverlayController::kOverlayId),
-                         WaitForWebContentsReady(
-                             kOverlayId, GURL("chrome-untrusted://lens")))),
+      InAnyContext(Steps(
+          InstrumentNonTabWebView(kOverlayId,
+                                  LensOverlayController::kOverlayId),
+          WaitForWebContentsReady(
+              kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
       // Wait for the webview to finish loading to prevent re-entrancy. Then
       // click a word to highlight it. Flush tasks after click to prevent
       // flakiness.
@@ -544,10 +548,11 @@
       // The overlay controller is an independent floating widget
       // associated with a tab rather than a browser window, so by
       // convention gets its own element context.
-      InAnyContext(Steps(InstrumentNonTabWebView(
-                             kOverlayId, LensOverlayController::kOverlayId),
-                         WaitForWebContentsReady(
-                             kOverlayId, GURL("chrome-untrusted://lens")))),
+      InAnyContext(Steps(
+          InstrumentNonTabWebView(kOverlayId,
+                                  LensOverlayController::kOverlayId),
+          WaitForWebContentsReady(
+              kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
       // Wait for the webview to finish loading to prevent re-entrancy. Then do
       // a drag offset from the center. Flush tasks after drag to prevent
       // flakiness.
@@ -614,10 +619,11 @@
       // The overlay controller is an independent floating widget
       // associated with a tab rather than a browser window, so by
       // convention gets its own element context.
-      InAnyContext(Steps(InstrumentNonTabWebView(
-                             kOverlayId, LensOverlayController::kOverlayId),
-                         WaitForWebContentsReady(
-                             kOverlayId, GURL("chrome-untrusted://lens")))),
+      InAnyContext(Steps(
+          InstrumentNonTabWebView(kOverlayId,
+                                  LensOverlayController::kOverlayId),
+          WaitForWebContentsReady(
+              kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
       // Wait for the webview to finish loading to prevent re-entrancy. Then do
       // a drag offset from the center. Flush tasks after drag to prevent
       // flakiness.
@@ -670,10 +676,11 @@
       // The overlay controller is an independent floating widget
       // associated with a tab rather than a browser window, so by
       // convention gets its own element context.
-      InAnyContext(Steps(InstrumentNonTabWebView(
-                             kOverlayId, LensOverlayController::kOverlayId),
-                         WaitForWebContentsReady(
-                             kOverlayId, GURL("chrome-untrusted://lens")))),
+      InAnyContext(Steps(
+          InstrumentNonTabWebView(kOverlayId,
+                                  LensOverlayController::kOverlayId),
+          WaitForWebContentsReady(
+              kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
 
       // The side panel should open with the results frame.
       InAnyContext(Steps(
@@ -719,10 +726,11 @@
       // The overlay controller is an independent floating widget
       // associated with a tab rather than a browser window, so by
       // convention gets its own element context.
-      InAnyContext(Steps(InstrumentNonTabWebView(
-                             kOverlayId, LensOverlayController::kOverlayId),
-                         WaitForWebContentsReady(
-                             kOverlayId, GURL("chrome-untrusted://lens")))),
+      InAnyContext(Steps(
+          InstrumentNonTabWebView(kOverlayId,
+                                  LensOverlayController::kOverlayId),
+          WaitForWebContentsReady(
+              kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
 
       // The side panel should open with the results frame.
       InAnyContext(Steps(
@@ -784,10 +792,11 @@
       // The overlay controller is an independent floating widget
       // associated with a tab rather than a browser window, so by
       // convention gets its own element context.
-      InAnyContext(Steps(InstrumentNonTabWebView(
-                             kOverlayId, LensOverlayController::kOverlayId),
-                         WaitForWebContentsReady(
-                             kOverlayId, GURL("chrome-untrusted://lens")))),
+      InAnyContext(Steps(
+          InstrumentNonTabWebView(kOverlayId,
+                                  LensOverlayController::kOverlayId),
+          WaitForWebContentsReady(
+              kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
       // Wait for the webview to finish loading to prevent re-entrancy. Then do
       // a drag offset from the center. Flush tasks after drag to prevent
       // flakiness.
diff --git a/chrome/browser/ui/lens/lens_overlay_request_id_generator.cc b/chrome/browser/ui/lens/lens_overlay_request_id_generator.cc
index 917b27e..8acc18b1 100644
--- a/chrome/browser/ui/lens/lens_overlay_request_id_generator.cc
+++ b/chrome/browser/ui/lens/lens_overlay_request_id_generator.cc
@@ -27,9 +27,7 @@
 }
 
 void LensOverlayRequestIdGenerator::CreateNewAnalyticsId() {
-  std::array<uint8_t, kAnalyticsIdBytesSize> bytes;
-  base::RandBytes(bytes);
-  analytics_id_ = std::string(bytes.begin(), bytes.end());
+  analytics_id_ = base::RandBytesAsString(kAnalyticsIdBytesSize);
 }
 
 std::unique_ptr<lens::LensOverlayRequestId>
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
index d96de96..9825423 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/ui/lens/lens_overlay_invocation_source.h"
 #include "chrome/browser/ui/lens/lens_overlay_side_panel_web_view.h"
 #include "chrome/browser/ui/lens/lens_overlay_url_builder.h"
-#include "chrome/browser/ui/lens/lens_untrusted_ui.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/tabs/public/tab_interface.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_content_proxy.h"
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.cc b/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.cc
index a61bb727..9614c27 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.cc
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.cc
@@ -9,16 +9,15 @@
 #include "chrome/browser/ui/lens/lens_overlay_controller.h"
 #include "chrome/browser/ui/lens/lens_overlay_event_handler.h"
 #include "chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h"
-#include "chrome/browser/ui/lens/lens_untrusted_ui.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/input/native_web_keyboard_event.h"
 #include "content/public/browser/file_select_listener.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 
-using SidePanelWebUIViewT_LensUntrustedUI =
-    SidePanelWebUIViewT<lens::LensUntrustedUI>;
-BEGIN_TEMPLATE_METADATA(SidePanelWebUIViewT_LensUntrustedUI,
+using SidePanelWebUIViewT_LensSidePanelUntrustedUI =
+    SidePanelWebUIViewT<lens::LensSidePanelUntrustedUI>;
+BEGIN_TEMPLATE_METADATA(SidePanelWebUIViewT_LensSidePanelUntrustedUI,
                         SidePanelWebUIViewT)
 END_METADATA
 
@@ -32,8 +31,9 @@
     : SidePanelWebUIViewT(
           base::RepeatingClosure(),
           base::RepeatingClosure(),
-          std::make_unique<WebUIContentsWrapperT<lens::LensUntrustedUI>>(
-              GURL(chrome::kChromeUILensUntrustedSidePanelURL),
+          std::make_unique<
+              WebUIContentsWrapperT<lens::LensSidePanelUntrustedUI>>(
+              GURL(chrome::kChromeUILensUntrustedSidePanelAPIURL),
               Profile::FromBrowserContext(browser_context),
               /*task_manager_string_id=*/IDS_SIDE_PANEL_COMPANION_TITLE,
               /*esc_closes_ui=*/false)),
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.h b/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.h
index 0d227aea..cc19970 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.h
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.h
@@ -7,7 +7,7 @@
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
-#include "chrome/browser/ui/lens/lens_untrusted_ui.h"
+#include "chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h"
 #include "content/public/browser/file_select_listener.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -26,11 +26,11 @@
 // that separates it from other views. This includes context menu support and
 // opening urls in a new tab.
 class LensOverlaySidePanelWebView
-    : public SidePanelWebUIViewT<lens::LensUntrustedUI> {
-  using SidePanelWebUIViewT_LensUntrustedUI =
-      SidePanelWebUIViewT<lens::LensUntrustedUI>;
+    : public SidePanelWebUIViewT<lens::LensSidePanelUntrustedUI> {
+  using SidePanelWebUIViewT_LensSidePanelUntrustedUI =
+      SidePanelWebUIViewT<lens::LensSidePanelUntrustedUI>;
   METADATA_HEADER(LensOverlaySidePanelWebView,
-                  SidePanelWebUIViewT_LensUntrustedUI)
+                  SidePanelWebUIViewT_LensSidePanelUntrustedUI)
 
  public:
   LensOverlaySidePanelWebView(
diff --git a/chrome/browser/ui/lens/lens_untrusted_ui.cc b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
similarity index 75%
rename from chrome/browser/ui/lens/lens_untrusted_ui.cc
rename to chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
index e14c0f97e..0ae66efa 100644
--- a/chrome/browser/ui/lens/lens_untrusted_ui.cc
+++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
@@ -7,7 +7,7 @@
 #pragma allow_unsafe_buffers
 #endif
 
-#include "chrome/browser/ui/lens/lens_untrusted_ui.h"
+#include "chrome/browser/ui/lens/lens_overlay_untrusted_ui.h"
 
 #include "base/strings/strcat.h"
 #include "chrome/browser/profiles/profile.h"
@@ -35,31 +35,30 @@
 // The number of times to show cursor tooltips.
 constexpr int kNumTimesToShowCursorTooltips = 5;
 
-LensUntrustedUI::LensUntrustedUI(content::WebUI* web_ui)
+LensOverlayUntrustedUI::LensOverlayUntrustedUI(content::WebUI* web_ui)
     : UntrustedTopChromeWebUIController(web_ui) {
-  // This code path is invoked for both the overlay WebUI and the sidepanel
-  // WebUI.
-
-  // Set up the chrome-untrusted://lens source.
+  // Set up the chrome-untrusted://lens-overlay source.
   content::WebUIDataSource* html_source =
       content::WebUIDataSource::CreateAndAdd(
           web_ui->GetWebContents()->GetBrowserContext(),
-          chrome::kChromeUILensUntrustedURL);
+          chrome::kChromeUILensOverlayUntrustedURL);
   html_source->AddLocalizedString("backButton", IDS_ACCNAME_BACK);
   html_source->AddLocalizedString("close", IDS_CLOSE);
   html_source->AddLocalizedString("copy", IDS_LENS_OVERLAY_COPY);
+  html_source->AddLocalizedString("copyAsImage",
+                                  IDS_LENS_OVERLAY_COPY_AS_IMAGE);
+  html_source->AddLocalizedString("copyAsImageToastMessage",
+                                  IDS_LENS_OVERLAY_COPY_AS_IMAGE_TOAST_MESSAGE);
   html_source->AddLocalizedString("copyToastMessage",
                                   IDS_LENS_OVERLAY_COPY_TOAST_MESSAGE);
   html_source->AddLocalizedString("dismiss",
                                   IDS_LENS_OVERLAY_TOAST_DISMISS_MESSAGE);
   html_source->AddLocalizedString("learnMore", IDS_LENS_OVERLAY_LEARN_MORE);
-  html_source->AddLocalizedString("initialToastLabel",
-                                  IDS_LENS_OVERLAY_INITIAL_TOAST_LABEL);
-  html_source->AddLocalizedString("initialToastMessage",
-                                  IDS_LENS_OVERLAY_INITIAL_TOAST_MESSAGE);
   html_source->AddLocalizedString("moreOptions",
                                   IDS_LENS_OVERLAY_MORE_OPTIONS_BUTTON_LABEL);
   html_source->AddLocalizedString("myActivity", IDS_LENS_OVERLAY_MY_ACTIVITY);
+  html_source->AddLocalizedString("saveAsImage",
+                                  IDS_LENS_OVERLAY_SAVE_AS_IMAGE);
   html_source->AddLocalizedString("sendFeedback", IDS_LENS_SEND_FEEDBACK);
   html_source->AddLocalizedString("cursorTooltipDragMessage",
                                   IDS_LENS_OVERLAY_CURSOR_TOOLTIP_DRAG_MESSAGE);
@@ -117,11 +116,6 @@
                           palette.at(lens::ColorId::kSelectionElement));
 
   // Add finch flags
-  html_source->AddString(
-      "resultsLoadingUrl",
-      lens::features::GetLensOverlayResultsSearchLoadingURL(
-          lens::LensOverlayShouldUseDarkMode(
-              ThemeServiceFactory::GetForProfile(Profile::FromWebUI(web_ui)))));
   html_source->AddBoolean("enableDebuggingMode",
                           lens::features::IsLensOverlayDebuggingEnabled());
   html_source->AddBoolean("enableShimmer",
@@ -137,10 +131,6 @@
                           lens::features::GetLensOverlayTapRegionHeight());
   html_source->AddInteger("tapRegionWidth",
                           lens::features::GetLensOverlayTapRegionWidth());
-  html_source->AddBoolean(
-      "darkMode",
-      lens::LensOverlayShouldUseDarkMode(
-          ThemeServiceFactory::GetForProfile(Profile::FromWebUI(web_ui))));
   html_source->AddDouble(
       "selectTextTriggerThreshold",
       lens::features::GetLensOverlaySelectTextOverRegionTriggerThreshold());
@@ -149,22 +139,23 @@
   html_source->AddDouble(
       "postSelectionComparisonThreshold",
       lens::features::GetLensOverlayPostSelectionComparisonThreshold());
-  html_source->AddBoolean("enableErrorPage",
-                          lens::features::GetLensOverlayEnableErrorPage());
   html_source->AddInteger(
       "segmentationMaskCornerRadius",
       lens::features::GetLensOverlaySegmentationMaskCornerRadius());
   html_source->AddBoolean(
       "enableOverlayTranslateButton",
       lens::features::IsLensOverlayTranslateButtonEnabled());
+  html_source->AddBoolean("enableCopyAsImage",
+                          lens::features::IsLensOverlayCopyAsImageEnabled());
+  html_source->AddBoolean("enableSaveAsImage",
+                          lens::features::IsLensOverlaySaveAsImageEnabled());
+  html_source->AddInteger(
+      "textReceivedTimeout",
+      lens::features::
+          GetLensOverlayImageContextMenuActionsTextReceivedTimeout());
 
-  // Two instances of LensUntrustedUI are constructed: one for the main overlay
-  // and one for the side panel. We cannot distinguish them at this time. As a
-  // hack, we try to look up the LensOverlayController, which will only be
-  // available for the main overlay, and use that to set state only used by the
-  // main overlay.
-  // TODO(b/354802414): Split this into 2 separate classes for overlay and
-  // side panel.
+  // Controller doesn't exist in unsupported context but WebUI should still
+  // load.
   if (auto* controller =
           LensOverlayController::GetControllerFromWebViewWebContents(
               web_ui->GetWebContents())) {
@@ -222,38 +213,19 @@
       lens_overlay_start_count <= kNumTimesToShowCursorTooltips);
 }
 
-void LensUntrustedUI::BindInterface(
+void LensOverlayUntrustedUI::BindInterface(
     mojo::PendingReceiver<lens::mojom::LensPageHandlerFactory> receiver) {
   lens_page_factory_receiver_.reset();
   lens_page_factory_receiver_.Bind(std::move(receiver));
 }
 
-void LensUntrustedUI::BindInterface(
-    mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
-  LensOverlayController* controller =
-      LensOverlayController::GetController(web_ui());
-  // TODO(crbug.com/360724768): This should not need to be null-checked and
-  // exists here as a temporary solution to handle situations where lens may be
-  // loaded in an unsupported context (e.g. browser tab). Remove this once work
-  // to restrict WebUI loading to relevant contexts has landed.
-  if (!controller) {
-    return;
-  }
-  auto handler = std::make_unique<RealboxHandler>(
-      std::move(receiver), Profile::FromWebUI(web_ui()),
-      web_ui()->GetWebContents(),
-      /*metrics_reporter=*/nullptr, /*lens_searchbox_client=*/controller,
-      /*omnibox_controller=*/nullptr);
-  controller->SetSearchboxHandler(std::move(handler));
-}
-
-void LensUntrustedUI::BindInterface(
+void LensOverlayUntrustedUI::BindInterface(
     mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
   color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
       web_ui()->GetWebContents(), std::move(receiver));
 }
 
-void LensUntrustedUI::CreatePageHandler(
+void LensOverlayUntrustedUI::CreatePageHandler(
     mojo::PendingReceiver<lens::mojom::LensPageHandler> receiver,
     mojo::PendingRemote<lens::mojom::LensPage> page) {
   LensOverlayController* controller =
@@ -270,25 +242,8 @@
   controller->BindOverlay(std::move(receiver), std::move(page));
 }
 
-void LensUntrustedUI::CreateSidePanelPageHandler(
-    mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandler> receiver,
-    mojo::PendingRemote<lens::mojom::LensSidePanelPage> page) {
-  LensOverlayController* controller =
-      LensOverlayController::GetController(web_ui());
-  // TODO(crbug.com/360724768): This should not need to be null-checked and
-  // exists here as a temporary solution to handle situations where lens may be
-  // loaded in an unsupported context (e.g. browser tab). Remove this once work
-  // to restrict WebUI loading to relevant contexts has landed.
-  if (!controller) {
-    return;
-  }
-  // Once the interface is bound, we want to connect this instance with the
-  // appropriate instance of LensOverlayController.
-  controller->BindSidePanel(std::move(receiver), std::move(page));
-}
+LensOverlayUntrustedUI::~LensOverlayUntrustedUI() = default;
 
-LensUntrustedUI::~LensUntrustedUI() = default;
-
-WEB_UI_CONTROLLER_TYPE_IMPL(LensUntrustedUI)
+WEB_UI_CONTROLLER_TYPE_IMPL(LensOverlayUntrustedUI)
 
 }  // namespace lens
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h
new file mode 100644
index 0000000..bd2c5fcc
--- /dev/null
+++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h
@@ -0,0 +1,74 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_LENS_LENS_OVERLAY_UNTRUSTED_UI_H_
+#define CHROME_BROWSER_UI_LENS_LENS_OVERLAY_UNTRUSTED_UI_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/lens/core/mojom/lens.mojom.h"
+#include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h"
+#include "chrome/browser/ui/webui/top_chrome/untrusted_top_chrome_web_ui_controller.h"
+#include "chrome/common/webui_url_constants.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
+#include "ui/webui/resources/cr_components/searchbox/searchbox.mojom-forward.h"
+
+namespace ui {
+class ColorChangeHandler;
+}
+
+namespace lens {
+class LensOverlayUntrustedUI;
+
+class LensOverlayUntrustedUIConfig
+    : public DefaultTopChromeWebUIConfig<LensOverlayUntrustedUI> {
+ public:
+  LensOverlayUntrustedUIConfig()
+      : DefaultTopChromeWebUIConfig(content::kChromeUIUntrustedScheme,
+                                    chrome::kChromeUILensOverlayHost) {}
+};
+
+// WebUI controller for the chrome-untrusted://lens-overlay page.
+class LensOverlayUntrustedUI : public UntrustedTopChromeWebUIController,
+                               public lens::mojom::LensPageHandlerFactory {
+ public:
+  explicit LensOverlayUntrustedUI(content::WebUI* web_ui);
+
+  LensOverlayUntrustedUI(const LensOverlayUntrustedUI&) = delete;
+  LensOverlayUntrustedUI& operator=(const LensOverlayUntrustedUI&) = delete;
+  ~LensOverlayUntrustedUI() override;
+
+  // Instantiates the implementor of the mojom::PageHandlerFactory mojo
+  // interface passing the pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<lens::mojom::LensPageHandlerFactory> receiver);
+
+  // Instantiates the implementor of the
+  // color_change_listener::mojom::PageHandler mojo interface passing the
+  // pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+          receiver);
+
+  static constexpr std::string GetWebUIName() { return "LensOverlayUntrusted"; }
+
+ private:
+  // lens::mojom::LensPageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingReceiver<lens::mojom::LensPageHandler> receiver,
+      mojo::PendingRemote<lens::mojom::LensPage> page) override;
+
+  std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
+
+  mojo::Receiver<lens::mojom::LensPageHandlerFactory>
+      lens_page_factory_receiver_{this};
+
+  base::WeakPtrFactory<LensOverlayUntrustedUI> weak_factory_{this};
+
+  WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+}  // namespace lens
+#endif  // CHROME_BROWSER_UI_LENS_LENS_OVERLAY_UNTRUSTED_UI_H_
diff --git a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
new file mode 100644
index 0000000..abba5ab
--- /dev/null
+++ b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
@@ -0,0 +1,157 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h"
+
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
+#include "base/strings/strcat.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/lens/lens_overlay_controller.h"
+#include "chrome/browser/ui/lens/lens_overlay_theme_utils.h"
+#include "chrome/browser/ui/webui/searchbox/realbox_handler.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/lens_shared_resources.h"
+#include "chrome/grit/lens_shared_resources_map.h"
+#include "chrome/grit/lens_untrusted_resources.h"
+#include "chrome/grit/lens_untrusted_resources_map.h"
+#include "components/lens/lens_features.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/webui/color_change_listener/color_change_handler.h"
+
+namespace lens {
+
+LensSidePanelUntrustedUI::LensSidePanelUntrustedUI(content::WebUI* web_ui)
+    : UntrustedTopChromeWebUIController(web_ui) {
+  // Set up the chrome-untrusted://lens/ source.
+  content::WebUIDataSource* html_source =
+      content::WebUIDataSource::CreateAndAdd(
+          web_ui->GetWebContents()->GetBrowserContext(),
+          chrome::kChromeUILensUntrustedSidePanelURL);
+  html_source->AddLocalizedString("backButton", IDS_ACCNAME_BACK);
+  html_source->AddLocalizedString(
+      "networkErrorPageTopLine",
+      IDS_SIDE_PANEL_COMPANION_ERROR_PAGE_FIRST_LINE);
+  html_source->AddLocalizedString(
+      "networkErrorPageBottomLine",
+      IDS_SIDE_PANEL_COMPANION_ERROR_PAGE_SECOND_LINE);
+
+  // Add finch flags
+  html_source->AddString(
+      "resultsLoadingUrl",
+      lens::features::GetLensOverlayResultsSearchLoadingURL(
+          lens::LensOverlayShouldUseDarkMode(
+              ThemeServiceFactory::GetForProfile(Profile::FromWebUI(web_ui)))));
+  html_source->AddBoolean(
+      "darkMode",
+      lens::LensOverlayShouldUseDarkMode(
+          ThemeServiceFactory::GetForProfile(Profile::FromWebUI(web_ui))));
+  html_source->AddBoolean("enableErrorPage",
+                          lens::features::GetLensOverlayEnableErrorPage());
+
+  // Allow FrameSrc from all Google subdomains as redirects can occur.
+  GURL results_side_panel_url =
+      GURL(lens::features::GetLensOverlayResultsSearchURL());
+  std::string frame_src_directive =
+      base::StrCat({"frame-src https://*.google.com ",
+                    results_side_panel_url.GetWithEmptyPath().spec(), ";"});
+  html_source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::FrameSrc, frame_src_directive);
+
+  // Allow ImgSrc and StyleSrc from chrome-untrusted:// paths for searchbox use.
+  // Allow data URLs to load in WebUI for full page screenshot.
+  html_source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::ImgSrc,
+      "img-src 'self' chrome-untrusted://resources "
+      "https://www.gstatic.com data: blob:;");
+  html_source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::StyleSrc,
+      "style-src 'self' chrome-untrusted://resources chrome-untrusted://theme");
+
+  // Add required resources.
+  webui::SetupWebUIDataSource(
+      html_source,
+      base::make_span(kLensUntrustedResources, kLensUntrustedResourcesSize),
+      IDR_LENS_UNTRUSTED_SIDE_PANEL_SIDE_PANEL_HTML);
+
+  html_source->AddResourcePaths(
+      base::make_span(kLensSharedResources, kLensSharedResourcesSize));
+
+  // Add required resources for the searchbox.
+  SearchboxHandler::SetupWebUIDataSource(html_source,
+                                         Profile::FromWebUI(web_ui));
+  html_source->AddString(
+      "searchboxDefaultIcon",
+      "//resources/cr_components/searchbox/icons/google_g.svg");
+  html_source->AddBoolean("reportMetrics", false);
+  html_source->AddLocalizedString("searchBoxHint",
+                                  IDS_GOOGLE_LENS_SEARCH_BOX_EMPTY_HINT);
+  html_source->AddLocalizedString("searchBoxHintMultimodal",
+                                  IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_MULTIMODAL);
+  html_source->AddBoolean("isLensSearchbox", true);
+}
+
+void LensSidePanelUntrustedUI::BindInterface(
+    mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandlerFactory>
+        receiver) {
+  lens_side_panel_page_factory_receiver_.reset();
+  lens_side_panel_page_factory_receiver_.Bind(std::move(receiver));
+}
+
+void LensSidePanelUntrustedUI::BindInterface(
+    mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
+  LensOverlayController* controller =
+      LensOverlayController::GetController(web_ui());
+  // TODO(crbug.com/360724768): This should not need to be null-checked and
+  // exists here as a temporary solution to handle situations where lens may be
+  // loaded in an unsupported context (e.g. browser tab). Remove this once work
+  // to restrict WebUI loading to relevant contexts has landed.
+  if (!controller) {
+    return;
+  }
+  auto handler = std::make_unique<RealboxHandler>(
+      std::move(receiver), Profile::FromWebUI(web_ui()),
+      web_ui()->GetWebContents(),
+      /*metrics_reporter=*/nullptr, /*lens_searchbox_client=*/controller,
+      /*omnibox_controller=*/nullptr);
+  controller->SetSearchboxHandler(std::move(handler));
+}
+
+void LensSidePanelUntrustedUI::BindInterface(
+    mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
+  color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
+      web_ui()->GetWebContents(), std::move(receiver));
+}
+
+void LensSidePanelUntrustedUI::CreateSidePanelPageHandler(
+    mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandler> receiver,
+    mojo::PendingRemote<lens::mojom::LensSidePanelPage> page) {
+  LensOverlayController* controller =
+      LensOverlayController::GetController(web_ui());
+  // TODO(crbug.com/360724768): This should not need to be null-checked and
+  // exists here as a temporary solution to handle situations where lens may be
+  // loaded in an unsupported context (e.g. browser tab). Remove this once work
+  // to restrict WebUI loading to relevant contexts has landed.
+  if (!controller) {
+    return;
+  }
+  // Once the interface is bound, we want to connect this instance with the
+  // appropriate instance of LensOverlayController.
+  controller->BindSidePanel(std::move(receiver), std::move(page));
+}
+
+LensSidePanelUntrustedUI::~LensSidePanelUntrustedUI() = default;
+
+WEB_UI_CONTROLLER_TYPE_IMPL(LensSidePanelUntrustedUI)
+
+}  // namespace lens
diff --git a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h
new file mode 100644
index 0000000..44e4569
--- /dev/null
+++ b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h
@@ -0,0 +1,86 @@
+// 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_UI_LENS_LENS_SIDE_PANEL_UNTRUSTED_UI_H_
+#define CHROME_BROWSER_UI_LENS_LENS_SIDE_PANEL_UNTRUSTED_UI_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/lens/core/mojom/lens.mojom.h"
+#include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h"
+#include "chrome/browser/ui/webui/top_chrome/untrusted_top_chrome_web_ui_controller.h"
+#include "chrome/common/webui_url_constants.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/url_constants.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
+#include "ui/webui/resources/cr_components/searchbox/searchbox.mojom-forward.h"
+
+namespace ui {
+class ColorChangeHandler;
+}
+
+namespace lens {
+
+class LensSidePanelUntrustedUI;
+
+class LensSidePanelUntrustedUIConfig
+    : public DefaultTopChromeWebUIConfig<LensSidePanelUntrustedUI> {
+ public:
+  LensSidePanelUntrustedUIConfig()
+      : DefaultTopChromeWebUIConfig(content::kChromeUIUntrustedScheme,
+                                    chrome::kChromeUILensSidePanelHost) {}
+};
+
+// WebUI controller for the chrome-untrusted://lens/ page.
+class LensSidePanelUntrustedUI
+    : public UntrustedTopChromeWebUIController,
+      public lens::mojom::LensSidePanelPageHandlerFactory {
+ public:
+  explicit LensSidePanelUntrustedUI(content::WebUI* web_ui);
+
+  LensSidePanelUntrustedUI(const LensSidePanelUntrustedUI&) = delete;
+  LensSidePanelUntrustedUI& operator=(const LensSidePanelUntrustedUI&) = delete;
+  ~LensSidePanelUntrustedUI() override;
+
+  // Instantiates the implementor of the mojom::PageHandlerFactory mojo
+  // interface passing the pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandlerFactory>
+          receiver);
+
+  // Instantiates the implementor of the searchbox::mojom::PageHandler mojo
+  // interface passing the pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver);
+
+  // Instantiates the implementor of the
+  // color_change_listener::mojom::PageHandler mojo interface passing the
+  // pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+          receiver);
+
+  static constexpr std::string GetWebUIName() {
+    return "LensSidePanelUntrusted";
+  }
+
+ private:
+  // lens::mojom::LensSidePanelPageHandlerFactory:
+  void CreateSidePanelPageHandler(
+      mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandler> receiver,
+      mojo::PendingRemote<lens::mojom::LensSidePanelPage> page) override;
+
+  std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
+
+  mojo::Receiver<lens::mojom::LensSidePanelPageHandlerFactory>
+      lens_side_panel_page_factory_receiver_{this};
+
+  base::WeakPtrFactory<LensSidePanelUntrustedUI> weak_factory_{this};
+
+  WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+}  // namespace lens
+
+#endif  // CHROME_BROWSER_UI_LENS_LENS_SIDE_PANEL_UNTRUSTED_UI_H_
diff --git a/chrome/browser/ui/lens/lens_untrusted_ui.h b/chrome/browser/ui/lens/lens_untrusted_ui.h
deleted file mode 100644
index 2cc33c40..0000000
--- a/chrome/browser/ui/lens/lens_untrusted_ui.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_LENS_LENS_UNTRUSTED_UI_H_
-#define CHROME_BROWSER_UI_LENS_LENS_UNTRUSTED_UI_H_
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/lens/core/mojom/lens.mojom.h"
-#include "chrome/browser/ui/webui/top_chrome/untrusted_top_chrome_web_ui_controller.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
-#include "ui/webui/resources/cr_components/searchbox/searchbox.mojom-forward.h"
-
-namespace ui {
-class ColorChangeHandler;
-}
-
-namespace lens {
-class LensPageHandler;
-
-// WebUI controller for the chrome-untrusted://lens page.
-class LensUntrustedUI : public UntrustedTopChromeWebUIController,
-                        public lens::mojom::LensPageHandlerFactory {
- public:
-  explicit LensUntrustedUI(content::WebUI* web_ui);
-
-  LensUntrustedUI(const LensUntrustedUI&) = delete;
-  LensUntrustedUI& operator=(const LensUntrustedUI&) = delete;
-  ~LensUntrustedUI() override;
-
-  // Instantiates the implementor of the mojom::PageHandlerFactory mojo
-  // interface passing the pending receiver that will be internally bound.
-  void BindInterface(
-      mojo::PendingReceiver<lens::mojom::LensPageHandlerFactory> receiver);
-
-  // Instantiates the implementor of the searchbox::mojom::PageHandler mojo
-  // interface passing the pending receiver that will be internally bound.
-  void BindInterface(
-      mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver);
-
-  // Instantiates the implementor of the
-  // color_change_listener::mojom::PageHandler mojo interface passing the
-  // pending receiver that will be internally bound.
-  void BindInterface(
-      mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
-          receiver);
-
-  static constexpr std::string GetWebUIName() { return "LensUntrusted"; }
-
- private:
-  // lens::mojom::LensPageHandlerFactory:
-  void CreatePageHandler(
-      mojo::PendingReceiver<lens::mojom::LensPageHandler> receiver,
-      mojo::PendingRemote<lens::mojom::LensPage> page) override;
-  void CreateSidePanelPageHandler(
-      mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandler> receiver,
-      mojo::PendingRemote<lens::mojom::LensSidePanelPage> page) override;
-
-  std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
-
-  mojo::Receiver<lens::mojom::LensPageHandlerFactory>
-      lens_page_factory_receiver_{this};
-
-  base::WeakPtrFactory<LensUntrustedUI> weak_factory_{this};
-
-  WEB_UI_CONTROLLER_TYPE_DECL();
-};
-
-}  // namespace lens
-#endif  // CHROME_BROWSER_UI_LENS_LENS_UNTRUSTED_UI_H_
diff --git a/chrome/browser/ui/lens/lens_untrusted_ui_config.cc b/chrome/browser/ui/lens/lens_untrusted_ui_config.cc
deleted file mode 100644
index 0293d23..0000000
--- a/chrome/browser/ui/lens/lens_untrusted_ui_config.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/lens/lens_untrusted_ui_config.h"
-
-#include "chrome/browser/ui/lens/lens_untrusted_ui.h"
-#include "chrome/common/webui_url_constants.h"
-#include "content/public/common/url_constants.h"
-
-namespace lens {
-
-LensUntrustedUIConfig::LensUntrustedUIConfig()
-    : DefaultTopChromeWebUIConfig(content::kChromeUIUntrustedScheme,
-                                  chrome::kChromeUILensHost) {}
-
-}  // namespace lens
diff --git a/chrome/browser/ui/lens/lens_untrusted_ui_config.h b/chrome/browser/ui/lens/lens_untrusted_ui_config.h
deleted file mode 100644
index 6f00acb..0000000
--- a/chrome/browser/ui/lens/lens_untrusted_ui_config.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_LENS_LENS_UNTRUSTED_UI_CONFIG_H_
-#define CHROME_BROWSER_UI_LENS_LENS_UNTRUSTED_UI_CONFIG_H_
-
-#include "chrome/browser/ui/lens/lens_untrusted_ui.h"
-#include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h"
-#include "content/public/common/url_constants.h"
-
-namespace lens {
-
-// The configuration for the chrome-untrusted://lens page.
-class LensUntrustedUIConfig
-    : public DefaultTopChromeWebUIConfig<LensUntrustedUI> {
- public:
-  LensUntrustedUIConfig();
-  ~LensUntrustedUIConfig() override = default;
-};
-
-}  // namespace lens
-
-#endif  // CHROME_BROWSER_UI_LENS_LENS_UNTRUSTED_UI_CONFIG_H_
diff --git a/chrome/browser/ui/profiles/profile_picker.h b/chrome/browser/ui/profiles/profile_picker.h
index 68b59f2..6f5beb5b 100644
--- a/chrome/browser/ui/profiles/profile_picker.h
+++ b/chrome/browser/ui/profiles/profile_picker.h
@@ -28,7 +28,7 @@
 }  // namespace views
 
 enum class StartupProfileModeReason;
-enum class ReauthUIError;
+class ForceSigninUIError;
 
 class ProfilePicker {
  public:
@@ -246,7 +246,7 @@
   // displayed through `on_error_callback`.
   static void SwitchToReauth(
       Profile* profile,
-      base::OnceCallback<void(ReauthUIError)> on_error_callback);
+      base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback);
 #endif
 
   // Switch to the flow that comes when the user decides to create a profile
diff --git a/chrome/browser/ui/signin/signin_view_controller_interactive_uitest.cc b/chrome/browser/ui/signin/signin_view_controller_interactive_uitest.cc
index 0313ab3..b2a6fc3 100644
--- a/chrome/browser/ui/signin/signin_view_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/signin/signin_view_controller_interactive_uitest.cc
@@ -353,7 +353,7 @@
   DiceWebSigninInterceptorDelegate delegate;
   WebSigninInterceptor::Delegate::BubbleParameters bubble_parameters(
       WebSigninInterceptor::SigninInterceptionType::kEnterpriseOIDC,
-      AccountInfo(), AccountInfo());
+      account_info, account_info);
 
   auto handle = delegate.ShowOidcInterceptionDialog(
       browser()->tab_strip_model()->GetActiveWebContents(), bubble_parameters,
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 127695e..fa03aff4 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -29,12 +29,12 @@
 #include "chrome/browser/content_settings/sound_content_setting_observer.h"
 #include "chrome/browser/dips/dips_bounce_detector.h"
 #include "chrome/browser/dips/dips_service.h"
+#include "chrome/browser/dips/stateful_bounce_counter.h"
 #include "chrome/browser/external_protocol/external_protocol_observer.h"
 #include "chrome/browser/favicon/favicon_utils.h"
 #include "chrome/browser/file_system_access/file_system_access_features.h"
 #include "chrome/browser/file_system_access/file_system_access_permission_request_manager.h"
 #include "chrome/browser/file_system_access/file_system_access_tab_helper.h"
-#include "chrome/browser/fingerprinting_protection/chrome_fingerprinting_protection_web_contents_helper_factory.h"
 #include "chrome/browser/history/history_tab_helper.h"
 #include "chrome/browser/history/top_sites_factory.h"
 #include "chrome/browser/history_clusters/history_clusters_tab_helper.h"
@@ -60,7 +60,6 @@
 #include "chrome/browser/predictors/loading_predictor_tab_helper.h"
 #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
 #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_tab_helper.h"
-#include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
@@ -133,7 +132,6 @@
 #include "components/download/content/factory/navigation_monitor_factory.h"
 #include "components/download/content/public/download_navigation_observer.h"
 #include "components/enterprise/buildflags/buildflags.h"
-#include "components/fingerprinting_protection_filter/common/fingerprinting_protection_filter_features.h"
 #include "components/history/content/browser/web_contents_top_sites_observer.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/infobars/content/content_infobar_manager.h"
@@ -398,6 +396,9 @@
   ConnectionHelpTabHelper::CreateForWebContents(web_contents);
   CoreTabHelper::CreateForWebContents(web_contents);
   DIPSWebContentsObserver::MaybeCreateForWebContents(web_contents);
+  if (auto* dips_wco = DIPSWebContentsObserver::FromWebContents(web_contents)) {
+    CHECK(dips::StatefulBounceCounter::Get(dips_wco));
+  }
 #if BUILDFLAG(ENABLE_REPORTING)
   if (base::FeatureList::IsEnabled(
           net::features::kReportingApiEnableEnterpriseCookieIssues)) {
@@ -410,14 +411,6 @@
   FileSystemAccessPermissionRequestManager::CreateForWebContents(web_contents);
   FileSystemAccessTabHelper::CreateForWebContents(web_contents);
   FindBarState::ConfigureWebContents(web_contents);
-  if (fingerprinting_protection_filter::features::
-          IsFingerprintingProtectionFeatureEnabled()) {
-    // TODO(https://crbug.com/40280666): Move this to TabFeatures.
-    CreateFingerprintingProtectionWebContentsHelper(
-        web_contents, profile->GetPrefs(),
-        TrackingProtectionSettingsFactory::GetForProfile(profile),
-        profile->IsIncognitoProfile());
-  }
   download::DownloadNavigationObserver::CreateForWebContents(
       web_contents,
       download::NavigationMonitorFactory::GetForKey(profile->GetProfileKey()));
diff --git a/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc b/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
index 14ec4c0..560f94b 100644
--- a/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
+++ b/chrome/browser/ui/tabs/organization/tab_declutter_controller.cc
@@ -90,13 +90,13 @@
   }
 
   if (DeclutterNudgeCriteriaMet(tabs)) {
-    if (!tabs.empty()) {
-      StartNudgeTimer();
-    }
-
+    StartNudgeTimer();
     for (auto& observer : observers_) {
       observer.OnTriggerDeclutterUIVisibility(!tabs.empty());
     }
+
+    stale_tabs_previous_nudge_.clear();
+    stale_tabs_previous_nudge_.insert(tabs.begin(), tabs.end());
   }
 }
 
@@ -106,6 +106,11 @@
     return false;
   }
 
+  // TODO(b/366078827): Handle hide case for the nudge.
+  if (stale_tabs.empty()) {
+    return false;
+  }
+
   const int total_tab_count = tab_strip_model_->GetTabCount();
 
   if (total_tab_count < kMinTabCountForNudge) {
@@ -119,7 +124,24 @@
     return false;
   }
 
-  return true;
+  // If there is a new stale tab found in this computation, return true.
+  for (tabs::TabModel* tab : stale_tabs) {
+    if (stale_tabs_previous_nudge_.count(tab) == 0) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void TabDeclutterController::OnActionUIAccepted(
+    base::PassKey<TabSearchContainer>) {
+  // TODO(shibalik): Reset backoff timer.
+}
+
+void TabDeclutterController::OnActionUIDismissed(
+    base::PassKey<TabSearchContainer>) {
+  // TODO(shibalik): Increment the backoff timer.
 }
 
 void TabDeclutterController::SetTimerForTesting(
diff --git a/chrome/browser/ui/tabs/organization/tab_declutter_controller.h b/chrome/browser/ui/tabs/organization/tab_declutter_controller.h
index a6b7263..a2360e1a 100644
--- a/chrome/browser/ui/tabs/organization/tab_declutter_controller.h
+++ b/chrome/browser/ui/tabs/organization/tab_declutter_controller.h
@@ -5,14 +5,17 @@
 #define CHROME_BROWSER_UI_TABS_ORGANIZATION_TAB_DECLUTTER_CONTROLLER_H_
 
 #include <memory>
+#include <set>
 
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "base/types/pass_key.h"
 #include "chrome/browser/ui/tabs/organization/tab_declutter_observer.h"
 
 class TabStripModel;
+class TabSearchContainer;
 
 namespace tabs {
 
@@ -48,6 +51,9 @@
     return nudge_timer_interval_minutes_;
   }
 
+  void OnActionUIAccepted(base::PassKey<TabSearchContainer>);
+  void OnActionUIDismissed(base::PassKey<TabSearchContainer>);
+
   void SetTimerForTesting(const base::TickClock* tick_clock,
                           scoped_refptr<base::SequencedTaskRunner> task_runner);
 
@@ -69,6 +75,9 @@
   // The timer that is responsible for blocking the nudge from showing.
   std::unique_ptr<base::OneShotTimer> nudge_timer_;
 
+  // The list of tabs shown previously in a nudge.
+  std::set<tabs::TabModel*> stale_tabs_previous_nudge_;
+
   base::ObserverList<TabDeclutterObserver> observers_;
   raw_ptr<TabStripModel> tab_strip_model_;
 };
diff --git a/chrome/browser/ui/tabs/organization/tab_declutter_controller_browsertest.cc b/chrome/browser/ui/tabs/organization/tab_declutter_controller_browsertest.cc
index 73f1f8d..0947ed7 100644
--- a/chrome/browser/ui/tabs/organization/tab_declutter_controller_browsertest.cc
+++ b/chrome/browser/ui/tabs/organization/tab_declutter_controller_browsertest.cc
@@ -161,6 +161,35 @@
 }
 
 IN_PROC_BROWSER_TEST_F(TabDeclutterControllerBrowserTest,
+                       TestTriggerDeclutterUIWhenNoNewStaleTabsFound) {
+  FakeTabDeclutterObserver fake_observer;
+  tab_declutter_controller_->AddObserver(&fake_observer);
+
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  base::TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner);
+
+  tab_declutter_controller_->SetTimerForTesting(task_runner->GetMockTickClock(),
+                                                task_runner);
+
+  // Add 12 tabs that are 2 days old (not stale) and 4 tabs that are 8 days old
+  // (stale).
+  AddTabsWithLastActiveTime(12, 2);
+  AddTabsWithLastActiveTime(4, 8);
+
+  // Move forward in time to simulate declutter timer triggering.
+  task_runner->FastForwardBy(
+      tab_declutter_controller_->nudge_timer_interval_minutes());
+
+  EXPECT_GE(fake_observer.stale_tabs_processed_count(), 1);
+  EXPECT_EQ(fake_observer.trigger_declutter_ui_visibility_count(), 1);
+
+  // Move forward in time to simulate declutter timer triggering.
+  task_runner->FastForwardBy(
+      tab_declutter_controller_->nudge_timer_interval_minutes());
+  EXPECT_EQ(fake_observer.trigger_declutter_ui_visibility_count(), 1);
+}
+
+IN_PROC_BROWSER_TEST_F(TabDeclutterControllerBrowserTest,
                        TestNudgeNotTriggeredWhenTabCountBelowThreshold) {
   FakeTabDeclutterObserver fake_observer;
   tab_declutter_controller_->AddObserver(&fake_observer);
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
index 4077670..071756e2 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
@@ -323,19 +323,7 @@
   UpdateGroupVisualData(saved_group.saved_guid(),
                         saved_group.local_group_id().value());
 
-  std::map<content::WebContents*, base::Uuid> contents_guid_mapping;
-  std::transform(
-      tab_guid_mapping.begin(), tab_guid_mapping.end(),
-      std::inserter(contents_guid_mapping, contents_guid_mapping.end()),
-      [](const std::pair<tabs::TabModel*, base::Uuid>& pair) {
-        // Transform the TabModel* to WebContents*
-        content::WebContents* web_contents = pair.first->contents();
-
-        // Return a pair with the transformed key and the same UUID value
-        return std::make_pair(web_contents, pair.second);
-      });
-
-  listener_->ConnectToLocalTabGroup(saved_group, contents_guid_mapping);
+  listener_->ConnectToLocalTabGroup(saved_group, tab_guid_mapping);
 
   return tab_group_id;
 }
@@ -366,17 +354,16 @@
   // Build the SavedTabGroupTabs and add them to the SavedTabGroup.
   const gfx::Range tab_range = tab_group->ListTabs();
 
-  std::map<content::WebContents*, base::Uuid> opened_web_contents_to_uuid;
+  std::map<tabs::TabModel*, base::Uuid> tab_guid_mapping;
   for (auto i = tab_range.start(); i < tab_range.end(); ++i) {
-    content::WebContents* web_contents = tab_strip_model->GetWebContentsAt(i);
-    CHECK(web_contents);
+    tabs::TabModel* tab = tab_strip_model->GetTabAtIndex(i);
+    CHECK(tab);
 
     SavedTabGroupTab saved_tab_group_tab =
         SavedTabGroupUtils::CreateSavedTabGroupTabFromWebContents(
-            web_contents, saved_tab_group.saved_guid());
+            tab->contents(), saved_tab_group.saved_guid());
 
-    opened_web_contents_to_uuid.emplace(web_contents,
-                                        saved_tab_group_tab.saved_tab_guid());
+    tab_guid_mapping.emplace(tab, saved_tab_group_tab.saved_tab_guid());
 
     saved_tab_group.AddTabLocally(std::move(saved_tab_group_tab));
   }
@@ -386,7 +373,7 @@
 
   // Link the local group to the saved group in the listener.
   listener_->ConnectToLocalTabGroup(*model_->Get(saved_group_guid),
-                                    opened_web_contents_to_uuid);
+                                    tab_guid_mapping);
 
   LogEvent(TabGroupEvent::kTabGroupCreated, saved_group_guid);
   return saved_group_guid;
@@ -468,23 +455,9 @@
   model_->OnGroupOpenedInTabStrip(saved_guid, local_group_id);
   UpdateGroupVisualData(saved_guid, local_group_id);
 
-  auto tab_guid_mapping =
-      GetTabToGuidMappingForSavedGroup(tab_strip_model, saved_group, tab_range);
-
-  std::map<content::WebContents*, base::Uuid> contents_guid_mapping;
-  std::transform(
-      tab_guid_mapping.begin(), tab_guid_mapping.end(),
-      std::inserter(contents_guid_mapping, contents_guid_mapping.end()),
-      [](const std::pair<tabs::TabModel*, base::Uuid>& pair) {
-        // Transform the TabModel* to WebContents*
-        content::WebContents* web_contents = pair.first->contents();
-
-        // Return a pair with the transformed key and the same UUID value
-        return std::make_pair(web_contents, pair.second);
-      });
-
-  listener_->ConnectToLocalTabGroup(*model_->Get(saved_guid),
-                                    contents_guid_mapping);
+  listener_->ConnectToLocalTabGroup(
+      *model_->Get(saved_guid), GetTabToGuidMappingForSavedGroup(
+                                    tab_strip_model, saved_group, tab_range));
 }
 
 void SavedTabGroupKeyedService::SavedTabGroupModelLoaded() {
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
index 17cf3cb..b1f65b2 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
@@ -70,20 +70,8 @@
       group_and_tab_guid_mapping.second;
   service_->AddGroup(std::move(copy_group));
 
-  std::map<content::WebContents*, base::Uuid> contents_guid_mapping;
-  std::transform(
-      tab_guid_mapping.begin(), tab_guid_mapping.end(),
-      std::inserter(contents_guid_mapping, contents_guid_mapping.end()),
-      [](const std::pair<tabs::TabModel*, base::Uuid>& pair) {
-        // Transform the TabModel* to WebContents*
-        content::WebContents* web_contents = pair.first->contents();
-
-        // Return a pair with the transformed key and the same UUID value
-        return std::make_pair(web_contents, pair.second);
-      });
-
   std::optional<SavedTabGroup> group = service_->GetGroup(group_id);
-  ConnectToLocalTabGroup(group.value(), std::move(contents_guid_mapping));
+  ConnectToLocalTabGroup(group.value(), tab_guid_mapping);
 }
 
 void SavedTabGroupModelListener::OnTabGroupWillBeRemoved(
@@ -250,20 +238,32 @@
 
 void SavedTabGroupModelListener::ConnectToLocalTabGroup(
     const SavedTabGroup& saved_tab_group,
-    std::map<content::WebContents*, base::Uuid> web_contents_map) {
+    std::map<tabs::TabModel*, base::Uuid> tab_guid_mapping) {
   const tab_groups::TabGroupId local_group_id =
       saved_tab_group.local_group_id().value();
 
-  // `web_contents_map` should have one entry per tab in the local group. This
+  // `tab_guid_mapping` should have one entry per tab in the local group. This
   // may not equal the saved group's size, if the saved group contains invalid
   // URLs.
   const size_t local_group_size =
       SavedTabGroupUtils::GetTabGroupWithId(local_group_id)->tab_count();
-  CHECK_EQ(local_group_size, web_contents_map.size());
+  CHECK_EQ(local_group_size, tab_guid_mapping.size());
+
+  std::map<content::WebContents*, base::Uuid> contents_guid_mapping;
+  std::transform(
+      tab_guid_mapping.begin(), tab_guid_mapping.end(),
+      std::inserter(contents_guid_mapping, contents_guid_mapping.end()),
+      [](const std::pair<tabs::TabModel*, base::Uuid>& pair) {
+        // Transform the TabModel* to WebContents*
+        content::WebContents* web_contents = pair.first->contents();
+
+        // Return a pair with the transformed key and the same UUID value
+        return std::make_pair(web_contents, pair.second);
+      });
 
   auto [iterator, success] = local_tab_group_listeners_.try_emplace(
       local_group_id, local_group_id, saved_tab_group.saved_guid(), service_,
-      web_contents_map);
+      contents_guid_mapping);
   CHECK(success);
 }
 
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
index f83e526f..3a964d9 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
@@ -61,7 +61,7 @@
   // corresponding local group.
   void ConnectToLocalTabGroup(
       const SavedTabGroup& saved_tab_group,
-      std::map<content::WebContents*, base::Uuid> web_contents_map);
+      std::map<tabs::TabModel*, base::Uuid> tab_guid_mapping);
 
   // Stop updating the saved group corresponding to the local group with id
   // `tab_group_id` when the local group changes.
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_delegate_desktop.cc b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_delegate_desktop.cc
index 8697c38..53dd0ed1 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_delegate_desktop.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_delegate_desktop.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "components/saved_tab_groups/tab_group_sync_service.h"
 #include "components/saved_tab_groups/types.h"
-#include "content/public/browser/web_contents.h"
 #include "ui/gfx/range/range.h"
 
 namespace tab_groups {
@@ -146,19 +145,7 @@
           tab, group.saved_tabs()[i - tab_range.start()].saved_tab_guid());
     }
 
-    std::map<content::WebContents*, base::Uuid> contents_guid_mapping;
-    std::transform(
-        tab_guid_mapping.begin(), tab_guid_mapping.end(),
-        std::inserter(contents_guid_mapping, contents_guid_mapping.end()),
-        [](const std::pair<tabs::TabModel*, base::Uuid>& pair) {
-          // Transform the TabModel* to WebContents*
-          content::WebContents* web_contents = pair.first->contents();
-
-          // Return a pair with the transformed key and the same UUID value
-          return std::make_pair(web_contents, pair.second);
-        });
-
-    listener_->ConnectToLocalTabGroup(group, std::move(contents_guid_mapping));
+    listener_->ConnectToLocalTabGroup(group, std::move(tab_guid_mapping));
   } else {
     listener_->UpdateLocalGroupFromSync(group_id);
   }
@@ -256,20 +243,7 @@
   const std::optional<SavedTabGroup> saved_group2 =
       service_->GetGroup(saved_group.saved_guid());
 
-  // TODO (later in chain) Remove.
-  std::map<content::WebContents*, base::Uuid> contents_guid_mapping;
-  std::transform(
-      tab_guid_mapping.begin(), tab_guid_mapping.end(),
-      std::inserter(contents_guid_mapping, contents_guid_mapping.end()),
-      [](const std::pair<tabs::TabModel*, base::Uuid>& pair) {
-        // Transform the TabModel* to WebContents*
-        content::WebContents* web_contents = pair.first->contents();
-
-        // Return a pair with the transformed key and the same UUID value
-        return std::make_pair(web_contents, pair.second);
-      });
-
-  listener_->ConnectToLocalTabGroup(*saved_group2, contents_guid_mapping);
+  listener_->ConnectToLocalTabGroup(*saved_group2, tab_guid_mapping);
   return tab_group_id;
 }
 }  // namespace tab_groups
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc
index 12cd22fd..39f7a08 100644
--- a/chrome/browser/ui/tabs/tab_features.cc
+++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -13,8 +13,10 @@
 #include "chrome/browser/commerce/shopping_service_factory.h"
 #include "chrome/browser/dips/dips_navigation_flow_detector_wrapper.h"
 #include "chrome/browser/enterprise/data_protection/data_protection_navigation_controller.h"
+#include "chrome/browser/fingerprinting_protection/chrome_fingerprinting_protection_web_contents_helper_factory.h"
 #include "chrome/browser/image_fetcher/image_fetcher_service_factory.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_tab_observer.h"
+#include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -31,6 +33,7 @@
 #include "chrome/browser/ui/views/webid/fedcm_account_selection_view_controller.h"
 #include "chrome/browser/user_annotations/user_annotations_web_contents_observer.h"
 #include "components/browsing_topics/browsing_topics_service.h"
+#include "components/fingerprinting_protection_filter/common/fingerprinting_protection_filter_features.h"
 #include "components/image_fetcher/core/image_fetcher_service.h"
 #include "components/permissions/permission_indicators_tab_data.h"
 #include "extensions/common/extension_features.h"
@@ -140,6 +143,14 @@
   read_anything_side_panel_controller_ =
       std::make_unique<ReadAnythingSidePanelController>(
           &tab, side_panel_registry_.get());
+
+  if (fingerprinting_protection_filter::features::
+          IsFingerprintingProtectionFeatureEnabled()) {
+    CreateFingerprintingProtectionWebContentsHelper(
+        tab.GetContents(), profile->GetPrefs(),
+        TrackingProtectionSettingsFactory::GetForProfile(profile),
+        profile->IsIncognitoProfile());
+  }
 }
 
 TabFeatures::TabFeatures() = default;
diff --git a/chrome/browser/ui/toasts/BUILD.gn b/chrome/browser/ui/toasts/BUILD.gn
index 65e161b..d2fb06f 100644
--- a/chrome/browser/ui/toasts/BUILD.gn
+++ b/chrome/browser/ui/toasts/BUILD.gn
@@ -15,6 +15,7 @@
   ]
   public_deps = [
     "//base",
+    "//chrome/browser/ui:browser_list",
     "//chrome/browser/ui/browser_window:browser_window",
     "//chrome/browser/ui/toasts/api:toasts",
     "//content/public/browser",
@@ -52,8 +53,10 @@
     ":toasts",
     "//base",
     "//base/test:test_support",
+    "//chrome/app:command_ids",
     "//chrome/browser",
     "//chrome/browser:browser_process",
+    "//chrome/browser/ui:browser_element_identifiers",
     "//chrome/browser/ui:ui",
     "//chrome/test:test_support",
     "//components/vector_icons",
@@ -61,9 +64,11 @@
     "//content/test:test_support",
     "//testing/gtest",
     "//ui/base:base",
+    "//ui/base:test_support",
     "//ui/gfx",
     "//ui/strings:ui_strings_grit",
     "//ui/views",
+    "//ui/views:test_support",
   ]
 }
 
diff --git a/chrome/browser/ui/toasts/api/toast_id.h b/chrome/browser/ui/toasts/api/toast_id.h
index 63d6284..de2b552 100644
--- a/chrome/browser/ui/toasts/api/toast_id.h
+++ b/chrome/browser/ui/toasts/api/toast_id.h
@@ -12,7 +12,8 @@
   kImageCopied = 1,
   kLinkToHighlightCopied = 2,
   kAddedToReadingList = 3,
-  kMax = kAddedToReadingList
+  kLensOverlay = 4,
+  kMax = kLensOverlay
 };
 
 #endif  // CHROME_BROWSER_UI_TOASTS_API_TOAST_ID_H_
diff --git a/chrome/browser/ui/toasts/toast_controller.cc b/chrome/browser/ui/toasts/toast_controller.cc
index 1350fd9..460cf84 100644
--- a/chrome/browser/ui/toasts/toast_controller.cc
+++ b/chrome/browser/ui/toasts/toast_controller.cc
@@ -15,6 +15,9 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/toasts/api/toast_id.h"
 #include "chrome/browser/ui/toasts/api/toast_registry.h"
@@ -22,6 +25,10 @@
 #include "chrome/browser/ui/toasts/toast_features.h"
 #include "chrome/browser/ui/toasts/toast_view.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
 
 ToastParams::ToastParams(ToastId id) : toast_id_(id) {}
 ToastParams::ToastParams(ToastParams&& other) noexcept = default;
@@ -32,12 +39,16 @@
     BrowserWindowInterface* browser_window_interface,
     const ToastRegistry* toast_registry)
     : browser_window_interface_(browser_window_interface),
-      toast_registry_(toast_registry) {}
+      toast_registry_(toast_registry) {
+  BrowserList::AddObserver(this);
+}
 
-ToastController::~ToastController() = default;
+ToastController::~ToastController() {
+  BrowserList::RemoveObserver(this);
+}
 
 bool ToastController::IsShowingToast() const {
-  return current_toast_params_.has_value();
+  return GetCurrentToastId().has_value();
 }
 
 bool ToastController::CanShowToast(ToastId id) const {
@@ -49,16 +60,17 @@
     return true;
   }
 
-  const ToastSpecification* current_toast_spec =
-      toast_registry_->GetToastSpecification(
-          current_toast_params_.value().toast_id_);
   const ToastSpecification* potential_toast_spec =
       toast_registry_->GetToastSpecification(id);
 
-  return !(current_toast_spec->is_persistent_toast() &&
+  return !(persistent_params_.has_value() &&
            potential_toast_spec->is_persistent_toast());
 }
 
+std::optional<ToastId> ToastController::GetCurrentToastId() const {
+  return currently_showing_toast_id_;
+}
+
 bool ToastController::MaybeShowToast(ToastParams params) {
   if (!CanShowToast(params.toast_id_)) {
     return false;
@@ -67,12 +79,7 @@
   CloseToast(toasts::ToastCloseReason::kPreempted);
 
   if (IsShowingToast()) {
-    // TODO(crbug.com/358610190): Record that a toast was preempted if
-    // `next_toast_params_` already have a value.
-
-    // Queue the params since we are waiting for the previous toast to
-    // fully close before showing the next toast.
-    next_toast_params_ = std::move(params);
+    QueueToast(std::move(params));
   } else {
     ShowToast(std::move(params));
   }
@@ -81,79 +88,147 @@
 }
 
 void ToastController::ClosePersistentToast(ToastId id) {
-  CHECK(current_toast_params_.has_value());
-  CHECK_EQ(current_toast_params_.value().toast_id_, id);
-  // TODO(crbug.com/358610787): close the persistent toast and have internal
-  // state reflect that.
-  CloseToast(toasts::ToastCloseReason::kAborted);
+  CHECK(persistent_params_.has_value());
+  CHECK_EQ(persistent_params_.value().toast_id_, id);
+  std::optional<ToastId> current_toast_id = GetCurrentToastId();
+  persistent_params_ = std::nullopt;
+
+  // Close the toast if we are currently showing a persistent toast.
+  if (current_toast_id.has_value() &&
+      toast_registry_->GetToastSpecification(current_toast_id.value())
+          ->is_persistent_toast()) {
+    CloseToast(toasts::ToastCloseReason::kFeatureDismiss);
+  }
 }
 
+#if BUILDFLAG(IS_MAC)
+void ToastController::OnWidgetActivationChanged(views::Widget* widget,
+                                                bool active) {
+  if (active) {
+    // On Mac, traversing out of the widget into the browser causes the browser
+    // to restore its focus to the wrong place. Thus, when entering the toast
+    // widget, make sure to clear out the browser's native focus. This causes
+    // the toast widget to lose activation, so reactivate it manually.
+    browser_window_interface_->TopContainer()
+        ->GetWidget()
+        ->GetFocusManager()
+        ->ClearNativeFocus();
+    toast_->GetWidget()->Activate();
+  }
+}
+#endif
+
 void ToastController::OnWidgetDestroyed(views::Widget* widget) {
-  current_toast_params_ = std::nullopt;
+  current_ephemeral_params_ = std::nullopt;
+  currently_showing_toast_id_ = std::nullopt;
   toast_ = nullptr;
   toast_observer_.Reset();
   toast_close_timer_.Stop();
 
-  // TODO(crbug.com/358610190): Record toast closed reason.
+  if (next_ephemeral_params_.has_value()) {
+    ShowToast(std::move(next_ephemeral_params_.value()));
+    next_ephemeral_params_ = std::nullopt;
+  } else if (persistent_params_.has_value()) {
+    ShowToast(std::move(persistent_params_.value()));
+  }
+}
 
-  // The previous toast is destroyed so we should show the next toast.
-  if (next_toast_params_.has_value()) {
-    ShowToast(std::move(next_toast_params_.value()));
-    next_toast_params_ = std::nullopt;
+void ToastController::OnBrowserClosing(Browser* browser) {
+  // Clear any queued toasts to prevent them from showing
+  // after an existing toast is destroyed.
+  if (browser_window_interface_ == browser) {
+    next_ephemeral_params_ = std::nullopt;
+    persistent_params_ = std::nullopt;
+  }
+}
+
+base::OneShotTimer* ToastController::GetToastCloseTimerForTesting() {
+  return &toast_close_timer_;
+}
+
+void ToastController::QueueToast(ToastParams params) {
+  if (next_ephemeral_params_.has_value()) {
+    // TODO(crbug.com/358610190): Record that next_ephemeral_params_ was
+    // preempted.
+    next_ephemeral_params_ = std::nullopt;
+  } else if (persistent_params_.has_value()) {
+    // TODO(crbug.com/358610190): Record that persistent_params_ was
+    // preempted.
+  } else {
+    // Since we are queuing a toast, current_ephemeral_params must have a value
+    // if we do not already have another ephemeral toast queued up.
+    CHECK(current_ephemeral_params_.has_value());
+    // TODO(crbug.com/358610190): Record that current_ephemeral_params was
+    // preempted.
+  }
+
+  if (toast_registry_->GetToastSpecification(params.toast_id_)
+          ->is_persistent_toast()) {
+    CHECK(!persistent_params_.has_value());
+    persistent_params_ = std::move(params);
+  } else {
+    next_ephemeral_params_ = std::move(params);
   }
 }
 
 void ToastController::ShowToast(ToastParams params) {
-  current_toast_params_ = std::move(params);
-
   const ToastSpecification* current_toast_spec =
-      toast_registry_->GetToastSpecification(
-          current_toast_params_.value().toast_id_);
-
+      toast_registry_->GetToastSpecification(params.toast_id_);
   CHECK(current_toast_spec);
-  if (!current_toast_spec->is_persistent_toast()) {
+
+  currently_showing_toast_id_ = params.toast_id_;
+  if (current_toast_spec->is_persistent_toast()) {
+    persistent_params_ = std::move(params);
+  } else {
+    current_ephemeral_params_ = std::move(params);
     toast_close_timer_.Start(
         FROM_HERE, toast_features::kToastTimeout.Get(),
         base::BindOnce(&ToastController::CloseToast, base::Unretained(this),
                        toasts::ToastCloseReason::kAutoDismissed));
   }
 
-  CreateToast(current_toast_spec);
+  CreateToast(current_toast_spec->is_persistent_toast()
+                  ? persistent_params_.value()
+                  : current_ephemeral_params_.value(),
+              current_toast_spec);
 }
 
 void ToastController::CloseToast(toasts::ToastCloseReason reason) {
-  if (!IsShowingToast()) {
-    return;
+  if (toast_) {
+    toast_->Close(reason);
   }
-
-  CHECK(toast_);
-  toast_->Close(reason);
 }
 
-void ToastController::CreateToast(const ToastSpecification* spec) {
-  if (!browser_window_interface_) {
-    CHECK_IS_TEST();
-    return;
-  }
-  CHECK(current_toast_params_.has_value());
-  std::unique_ptr<toasts::ToastView> toast =
-      std::make_unique<toasts::ToastView>(
-          browser_window_interface_->TopContainer(),
-          FormatString(spec->body_string_id(),
-                       current_toast_params_->body_string_replacement_params_),
-          spec->icon(), spec->has_close_button());
+void ToastController::CreateToast(const ToastParams& params,
+                                  const ToastSpecification* spec) {
+  views::View* anchor_view = browser_window_interface_->TopContainer();
+  CHECK(anchor_view);
+  auto toast_view = std::make_unique<toasts::ToastView>(
+      anchor_view,
+      FormatString(spec->body_string_id(),
+                   params.body_string_replacement_params_),
+      spec->icon(), spec->has_close_button());
+
   if (spec->action_button_string_id().has_value()) {
-    toast->AddActionButton(
-        FormatString(
-            spec->action_button_string_id().value(),
-            current_toast_params_->action_button_string_replacement_params_),
+    toast_view->AddActionButton(
+        FormatString(spec->action_button_string_id().value(),
+                     params.action_button_string_replacement_params_),
         spec->action_button_callback());
   }
-  toast_ = toast.get();
+  toast_ = toast_view.get();
   views::Widget* const toast_widget =
-      views::BubbleDialogDelegateView::CreateBubble(std::move(toast));
+      views::BubbleDialogDelegateView::CreateBubble(std::move(toast_view));
   toast_observer_.Observe(toast_widget);
   toast_widget->SetVisibilityChangedAnimationsEnabled(false);
+  // Set the the focus traversable parent of the toast widget to be the anchor
+  // view, so that when focus leaves the toast, the search for the next
+  // focusable view will start from the right place. However, does not set the
+  // anchor view's focus traversable to be the toast widget, because when focus
+  // leaves the toast widget it will go into the anchor view's focus traversable
+  // if it exists, so doing that would trap focus inside of the toast widget.
+  toast_widget->SetFocusTraversableParent(
+      anchor_view->GetWidget()->GetFocusTraversable());
+  toast_widget->SetFocusTraversableParentView(anchor_view);
   toast_widget->ShowInactive();
   toast_->AnimateIn();
 }
diff --git a/chrome/browser/ui/toasts/toast_controller.h b/chrome/browser/ui/toasts/toast_controller.h
index 211639e..c43128e 100644
--- a/chrome/browser/ui/toasts/toast_controller.h
+++ b/chrome/browser/ui/toasts/toast_controller.h
@@ -12,8 +12,10 @@
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
 #include "base/timer/timer.h"
+#include "chrome/browser/ui/browser_list_observer.h"
 #include "ui/views/widget/widget_observer.h"
 
+class Browser;
 class BrowserWindowInterface;
 class ToastRegistry;
 class ToastSpecification;
@@ -35,7 +37,8 @@
   std::vector<std::u16string> action_button_string_replacement_params_;
 };
 
-class ToastController : public views::WidgetObserver {
+class ToastController : public views::WidgetObserver,
+                        public BrowserListObserver {
  public:
   explicit ToastController(BrowserWindowInterface* browser_window_interface,
                            const ToastRegistry* toast_registry);
@@ -43,6 +46,7 @@
 
   bool IsShowingToast() const;
   bool CanShowToast(ToastId id) const;
+  std::optional<ToastId> GetCurrentToastId() const;
 
   // Attempts to show the toast and returns true if the toast was successfully
   // shown, otherwise return false. Callers that show a persistent toast must
@@ -53,20 +57,31 @@
   void ClosePersistentToast(ToastId id);
 
   // views::WidgetObserver:
+#if BUILDFLAG(IS_MAC)
+  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
+#endif
   void OnWidgetDestroyed(views::Widget* widget) override;
 
+  // BrowserListObserver::
+  void OnBrowserClosing(Browser* browser) override;
+
+  base::OneShotTimer* GetToastCloseTimerForTesting();
+
  private:
+  void QueueToast(ToastParams params);
   void ShowToast(ToastParams params);
-  void CreateToast(const ToastSpecification*);
+  virtual void CreateToast(const ToastParams& params,
+                           const ToastSpecification* spec);
   virtual void CloseToast(toasts::ToastCloseReason reason);
-  void OnToastClosed();
   std::u16string FormatString(int string_id,
                               std::vector<std::u16string> replacement);
 
   const raw_ptr<BrowserWindowInterface> browser_window_interface_;
   const raw_ptr<const ToastRegistry> toast_registry_;
-  std::optional<ToastParams> current_toast_params_;
-  std::optional<ToastParams> next_toast_params_;
+  std::optional<ToastParams> current_ephemeral_params_;
+  std::optional<ToastParams> next_ephemeral_params_;
+  std::optional<ToastParams> persistent_params_;
+  std::optional<ToastId> currently_showing_toast_id_;
   base::OneShotTimer toast_close_timer_;
 
   // Observer to check when the toast is destroyed.
diff --git a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
index 55035797..e4b4843d 100644
--- a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
+++ b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
@@ -3,21 +3,31 @@
 // found in the LICENSE file.
 
 #include "base/test/scoped_feature_list.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/toasts/api/toast_id.h"
 #include "chrome/browser/ui/toasts/toast_controller.h"
 #include "chrome/browser/ui/toasts/toast_features.h"
 #include "chrome/browser/ui/toasts/toast_view.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
 #include "content/public/test/browser_test.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/interaction/interactive_test.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/interaction/interactive_views_test.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
 
 class ToastControllerInteractiveTest : public InteractiveBrowserTest {
  public:
   void SetUp() override {
     feature_list_.InitWithFeatures(
         {toast_features::kToastFramework, toast_features::kLinkCopiedToast,
-         toast_features::kImageCopiedToast},
+         toast_features::kImageCopiedToast, toast_features::kReadingListToast},
         {});
     InteractiveBrowserTest::SetUp();
   }
@@ -31,6 +41,23 @@
         [&]() { GetToastController()->MaybeShowToast(std::move(params)); });
   }
 
+  auto FireToastCloseTimer() {
+    return Do([=]() {
+      GetToastController()->GetToastCloseTimerForTesting()->FireNow();
+    });
+  }
+
+  auto CheckShowingToastId(ToastId expected_id) {
+    return CheckResult(
+        [=]() {
+          ToastController* const toast_controller = GetToastController();
+          std::optional<ToastId> current_toast_id =
+              toast_controller->GetCurrentToastId();
+          return current_toast_id.value();
+        },
+        expected_id);
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
@@ -60,3 +87,65 @@
                   }),
                   ShowToast(ToastParams(ToastId::kImageCopied)));
 }
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest, ShowPersistentToast) {
+  RunTestSequence(ShowToast(ToastParams(ToastId::kLensOverlay)),
+                  WaitForShow(toasts::ToastView::kToastViewId), Check([=]() {
+                    return GetToastController()->IsShowingToast();
+                  }));
+}
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest, PersistentToastHides) {
+  RunTestSequence(
+      ShowToast(ToastParams(ToastId::kLensOverlay)),
+      WaitForShow(toasts::ToastView::kToastViewId), Do([=]() {
+        GetToastController()->ClosePersistentToast(ToastId::kLensOverlay);
+      }),
+      WaitForHide(toasts::ToastView::kToastViewId));
+}
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest, PreemptPersistentToast) {
+  RunTestSequence(
+      ShowToast(ToastParams(ToastId::kLensOverlay)),
+      WaitForShow(toasts::ToastView::kToastViewId),
+      Check([=]() { return GetToastController()->IsShowingToast(); }),
+      CheckShowingToastId(ToastId::kLensOverlay),
+      ShowToast(ToastParams(ToastId::kLinkCopied)),
+      // Ephemeral Toast should force the persistent toast to close
+      WaitForHide(toasts::ToastView::kToastViewId),
+      // After the persistent toast closes, the ephemeral toast should show
+      WaitForShow(toasts::ToastView::kToastViewId),
+      CheckShowingToastId(ToastId::kLinkCopied),
+      // Simulate the ephemeral toast timing out and auto dismiss
+      FireToastCloseTimer(), WaitForHide(toasts::ToastView::kToastViewId),
+      // Persistent toast should reshow
+      WaitForShow(toasts::ToastView::kToastViewId),
+      CheckShowingToastId(ToastId::kLensOverlay));
+}
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest, FocusNextPane) {
+  ui::Accelerator next_pane;
+  ASSERT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())->GetAccelerator(
+      IDC_FOCUS_NEXT_PANE, &next_pane));
+  views::Widget* toast_widget = nullptr;
+  RunTestSequence(
+      ObserveState(views::test::kCurrentWidgetFocus),
+      ShowToast(ToastParams(ToastId::kAddedToReadingList)),
+      WaitForShow(toasts::ToastView::kToastViewId),
+      WithView(
+          toasts::ToastView::kToastViewId,
+          [&](toasts::ToastView* toast) { toast_widget = toast->GetWidget(); }),
+      CheckView(toasts::ToastView::kToastViewId,
+                [](toasts::ToastView* toast) {
+                  return !toast->GetFocusManager()->GetFocusedView();
+                }),
+      SendAccelerator(kBrowserViewElementId, next_pane),
+      WaitForState(views::test::kCurrentWidgetFocus,
+                   [&]() { return toast_widget->GetNativeView(); }),
+      CheckView(toasts::ToastView::kToastViewId, [](toasts::ToastView* toast) {
+        return toast->GetFocusManager()->GetFocusedView() ==
+               toast->action_button_for_testing();
+      }));
+}
+
+// TODO(crbug.com/358664193): Add tests for focus traversal using tab/shift-tab.
diff --git a/chrome/browser/ui/toasts/toast_controller_unittest.cc b/chrome/browser/ui/toasts/toast_controller_unittest.cc
index 6e9bab7..33c5167 100644
--- a/chrome/browser/ui/toasts/toast_controller_unittest.cc
+++ b/chrome/browser/ui/toasts/toast_controller_unittest.cc
@@ -12,7 +12,9 @@
 #include "chrome/browser/ui/toasts/api/toast_registry.h"
 #include "chrome/browser/ui/toasts/api/toast_specification.h"
 #include "chrome/browser/ui/toasts/toast_features.h"
+#include "chrome/browser/ui/toasts/toast_view.h"
 #include "components/vector_icons/vector_icons.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -26,6 +28,9 @@
       OnWidgetDestroyed(nullptr);
     }
   }
+
+  MOCK_METHOD2(CreateToast,
+               void(const ToastParams& params, const ToastSpecification* spec));
 };
 }  // namespace
 
@@ -62,7 +67,9 @@
   EXPECT_TRUE(controller->CanShowToast(ToastId::kLinkCopied));
 
   // We can show the toast again because it is an ephemeral toast.
+  EXPECT_CALL(*controller, CreateToast);
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
+  ::testing::Mock::VerifyAndClear(controller.get());
   EXPECT_TRUE(controller->IsShowingToast());
   EXPECT_TRUE(controller->CanShowToast(ToastId::kLinkCopied));
 }
@@ -84,7 +91,9 @@
 
   // We should be able to show the toast because there is no toast showing.
   EXPECT_TRUE(controller->CanShowToast(ToastId::kLinkCopied));
+  EXPECT_CALL(*controller, CreateToast);
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
+  ::testing::Mock::VerifyAndClear(controller.get());
   EXPECT_TRUE(controller->IsShowingToast());
 
   // We should not be able to trigger the same same toast to show or another
@@ -105,7 +114,9 @@
           .Build());
 
   auto controller = std::make_unique<TestToastController>(registry);
+  EXPECT_CALL(*controller, CreateToast);
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kImageCopied)));
+  ::testing::Mock::VerifyAndClear(controller.get());
   EXPECT_TRUE(controller->IsShowingToast());
 
   // The ephemeral toast can show but the persistent toast cannot show while we
@@ -122,7 +133,9 @@
   auto controller = std::make_unique<TestToastController>(registry);
 
   // We can show the toast again because it is an ephemeral toast.
+  EXPECT_CALL(*controller, CreateToast);
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
+  ::testing::Mock::VerifyAndClear(controller.get());
   EXPECT_TRUE(controller->IsShowingToast());
 
   // The toast should stop showing after reaching toast timeout time.
@@ -142,7 +155,9 @@
   auto controller = std::make_unique<TestToastController>(registry);
 
   // We can show the toast again because it is an ephemeral toast.
+  EXPECT_CALL(*controller, CreateToast);
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
+  ::testing::Mock::VerifyAndClear(controller.get());
   EXPECT_TRUE(controller->IsShowingToast());
 
   // The toast should still be showing because we didn't reach the time out time
@@ -151,7 +166,9 @@
   EXPECT_TRUE(controller->IsShowingToast());
 
   // Show a different toast before the link copied toast times out.
+  EXPECT_CALL(*controller, CreateToast);
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kImageCopied)));
+  ::testing::Mock::VerifyAndClear(controller.get());
   EXPECT_TRUE(controller->IsShowingToast());
 
   // The image copied toast should still be showing even though the link copied
@@ -169,7 +186,9 @@
 
   auto controller = std::make_unique<TestToastController>(registry);
 
+  EXPECT_CALL(*controller, CreateToast);
   EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
+  ::testing::Mock::VerifyAndClear(controller.get());
   EXPECT_TRUE(controller->IsShowingToast());
 
   // The toast should remain showing even after past the toast timeout time.
@@ -180,3 +199,23 @@
   controller->ClosePersistentToast(ToastId::kLinkCopied);
   EXPECT_FALSE(controller->IsShowingToast());
 }
+
+TEST_F(ToastControllerUnitTest, ClosePersistentToast) {
+  ToastRegistry* const registry = toast_registry();
+  registry->RegisterToast(ToastId::kLinkCopied, ToastSpecification::Builder(
+                                                    vector_icons::kEmailIcon, 0)
+                                                    .AddPersistance()
+                                                    .Build());
+
+  auto controller = std::make_unique<TestToastController>(registry);
+  EXPECT_CALL(*controller, CreateToast);
+  EXPECT_TRUE(controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
+  ::testing::Mock::VerifyAndClear(controller.get());
+  EXPECT_TRUE(controller->IsShowingToast());
+
+  controller->ClosePersistentToast(ToastId::kLinkCopied);
+  EXPECT_FALSE(controller->IsShowingToast());
+  // Trying to close the persistent toast should crash since the toast is
+  // already closed.
+  EXPECT_DEATH(controller->ClosePersistentToast(ToastId::kLinkCopied), "");
+}
diff --git a/chrome/browser/ui/toasts/toast_service.cc b/chrome/browser/ui/toasts/toast_service.cc
index fdbc00b..47989d2 100644
--- a/chrome/browser/ui/toasts/toast_service.cc
+++ b/chrome/browser/ui/toasts/toast_service.cc
@@ -64,4 +64,13 @@
                                base::Unretained(browser_window_interface)))
           .AddCloseButton()
           .Build());
+
+  // TODO(crbug.com/357929158): This registration only partially implements the
+  // Chromnient toast and will need to handle alternate icons and strings.
+  toast_registry_->RegisterToast(
+      ToastId::kLensOverlay,
+      ToastSpecification::Builder(vector_icons::kSearchChromeRefreshIcon,
+                                  IDS_LENS_OVERLAY_INITIAL_TOAST_MESSAGE)
+          .AddPersistance()
+          .Build());
 }
diff --git a/chrome/browser/ui/toasts/toast_view.cc b/chrome/browser/ui/toasts/toast_view.cc
index 3257dd6..3bfc79b 100644
--- a/chrome/browser/ui/toasts/toast_view.cc
+++ b/chrome/browser/ui/toasts/toast_view.cc
@@ -65,6 +65,7 @@
 
 void ToastView::AddActionButton(const std::u16string& action_button_text,
                                 base::RepeatingClosure action_button_callback) {
+  CHECK(!has_action_button_);
   has_action_button_ = true;
   action_button_text_ = action_button_text;
   action_button_callback_ = std::move(action_button_callback);
@@ -106,13 +107,14 @@
                                 ToastCloseReason::kActionButton)),
         action_button_text_));
     action_button_->SetEnabledTextColorIds(ui::kColorToastButton);
-    action_button_->SetBgColorIdOverride(ui::kColorToastBackground);
+    action_button_->SetBgColorIdOverride(ui::kColorToastBackgroundProminent);
     action_button_->SetStrokeColorIdOverride(ui::kColorToastButton);
     action_button_->SetPreferredSize(gfx::Size(
         action_button_->GetPreferredSize().width(),
         lp->GetDistanceMetric(DISTANCE_TOAST_BUBBLE_HEIGHT_ACTION_BUTTON)));
     action_button_->SetStyle(ui::ButtonStyle::kProminent);
     action_button_->GetViewAccessibility().SetRole(ax::mojom::Role::kAlert);
+    SetInitiallyFocusedView(action_button_);
   }
 
   if (has_close_button_) {
@@ -125,6 +127,9 @@
         ui::kColorToastForeground));
     views::InstallCircleHighlightPathGenerator(close_button_);
     close_button_->SetAccessibleName(l10n_util::GetStringUTF16(IDS_CLOSE));
+    if (!HasConfiguredInitiallyFocusedView()) {
+      SetInitiallyFocusedView(close_button_);
+    }
   }
 
   // Height of the toast is set implicitly by adding margins depending on the
@@ -142,6 +147,13 @@
   set_margins(gfx::Insets::TLBR(
       top_margin, lp->GetDistanceMetric(DISTANCE_TOAST_BUBBLE_MARGIN_LEFT),
       total_vertical_margins - top_margin, right_margin));
+
+  if (has_action_button_ || has_close_button_) {
+    SetFocusTraversesOut(true);
+  } else {
+    set_focus_traversable_from_anchor_view(false);
+    SetCanActivate(false);
+  }
 }
 
 void ToastView::AnimationProgressed(const gfx::Animation* animation) {
@@ -171,7 +183,7 @@
       GetScaleTransformation(bubble_frame_view->bounds()));
   bubble_frame_view->layer()->SetOpacity(0);
   GetDialogClientView()->SetBackground(
-      views::CreateThemedSolidBackground(ui::kColorToastBackground));
+      views::CreateThemedSolidBackground(ui::kColorToastBackgroundProminent));
   GetDialogClientView()->SetPaintToLayer();
   GetDialogClientView()->layer()->SetOpacity(0);
   views::AnimationBuilder()
@@ -226,7 +238,7 @@
 void ToastView::OnThemeChanged() {
   BubbleDialogDelegateView::OnThemeChanged();
   const auto* color_provider = GetColorProvider();
-  set_color(color_provider->GetColor(ui::kColorToastBackground));
+  set_color(color_provider->GetColor(ui::kColorToastBackgroundProminent));
   icon_view_->SetImage(ui::ImageModel::FromVectorIcon(
       *icon_, color_provider->GetColor(ui::kColorToastForeground),
       ChromeLayoutProvider::Get()->GetDistanceMetric(
diff --git a/chrome/browser/ui/toasts/toast_view.h b/chrome/browser/ui/toasts/toast_view.h
index ddb50187..180154e 100644
--- a/chrome/browser/ui/toasts/toast_view.h
+++ b/chrome/browser/ui/toasts/toast_view.h
@@ -27,8 +27,8 @@
   kCloseButton = 2,
   kPreempted = 3,
   kMenuItemClick = 4,
-  kAborted = 5,  // When the feature explicitly dismisses the toast.
-  kMaxValue = kAborted
+  kFeatureDismiss = 5,
+  kMaxValue = kFeatureDismiss
 };
 
 // The view for toasts.
diff --git a/chrome/browser/ui/views/autofill/save_address_profile_view.cc b/chrome/browser/ui/views/autofill/save_address_profile_view.cc
index b7db54a..5f93482 100644
--- a/chrome/browser/ui/views/autofill/save_address_profile_view.cc
+++ b/chrome/browser/ui/views/autofill/save_address_profile_view.cc
@@ -29,7 +29,6 @@
 #include "skia/ext/image_operations.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
-#include "ui/base/models/simple_combobox_model.h"
 #include "ui/base/mojom/dialog_button.mojom.h"
 #include "ui/color/color_id.h"
 #include "ui/gfx/canvas.h"
@@ -41,7 +40,6 @@
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
-#include "ui/views/controls/editable_combobox/editable_combobox.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/flex_layout.h"
@@ -58,13 +56,6 @@
 
 constexpr int kIconSize = 16;
 
-int ComboboxIconSize() {
-  // Use the line height of the body small text. This allows the icons to adapt
-  // if the user changes the font size.
-  return views::TypographyProvider::Get().GetLineHeight(
-      views::style::CONTEXT_MENU, views::style::STYLE_PRIMARY);
-}
-
 std::unique_ptr<views::ImageView> CreateAddressSectionIcon(
     const gfx::VectorIcon& icon) {
   auto icon_view = std::make_unique<views::ImageView>();
@@ -118,39 +109,6 @@
       .Build();
 }
 
-std::unique_ptr<views::EditableCombobox> CreateNicknameEditableCombobox() {
-  // TODO(crbug.com/40164487): Update the icons
-  // TODO(crbug.com/40164487): Use internationalized string.
-  ui::SimpleComboboxModel::Item home(
-      /*text=*/u"Home",
-      /*dropdown_secondary_text=*/std::u16string(),
-      /*icon=*/
-      ui::ImageModel::FromVectorIcon(kNavigateHomeIcon, ui::kColorIcon,
-                                     ComboboxIconSize()));
-
-  ui::SimpleComboboxModel::Item work(
-      /*text=*/u"Work",
-      /*dropdown_secondary_text=*/std::u16string(),
-      /*icon=*/
-      ui::ImageModel::FromVectorIcon(vector_icons::kBusinessIcon,
-                                     ui::kColorIcon, ComboboxIconSize()));
-
-  std::vector<ui::SimpleComboboxModel::Item> nicknames{std::move(home),
-                                                       std::move(work)};
-
-  auto combobox = std::make_unique<views::EditableCombobox>(
-      std::make_unique<ui::SimpleComboboxModel>(std::move(nicknames)),
-      /*filter_on_edit=*/true);
-
-  combobox->SetProperty(
-      views::kFlexBehaviorKey,
-      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
-                               views::MaximumFlexSizeRule::kUnbounded));
-  // TODO(crbug.com/40164487): Use internationalized string.
-  combobox->GetViewAccessibility().SetName(u"Address Label");
-  return combobox;
-}
-
 }  // namespace
 
 SaveAddressProfileView::SaveAddressProfileView(
@@ -261,15 +219,6 @@
         IDS_AUTOFILL_SAVE_PROMPT_EMAIL_SECTION_A11Y_LABEL);
   }
 
-  if (base::FeatureList::IsEnabled(
-          features::kAutofillAddressProfileSavePromptNicknameSupport)) {
-    // TODO(crbug.com/40164487): Make sure the icon is vertically centered with
-    // the editable combobox.
-    AddAddressSection(/*parent_view=*/address_components_view_,
-                      CreateAddressSectionIcon(vector_icons::kExtensionIcon),
-                      CreateNicknameEditableCombobox());
-  }
-
   std::u16string footer_message = controller_->GetFooterMessage();
   if (!footer_message.empty()) {
     SetFootnoteView(
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
index ade1a39..aec2c46b 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
@@ -538,6 +538,7 @@
     const base::Uuid& sync_id,
     const std::optional<LocalTabGroupID>& local_id) {
   SavedTabGroupUpdated(sync_id);
+  MaybeShowClosePromo(sync_id);
 }
 
 void SavedTabGroupBar::OnTabGroupRemoved(const base::Uuid& sync_id,
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_iph_promo_test.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_iph_promo_test.cc
index 9a16e29..3f01919 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_iph_promo_test.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_iph_promo_test.cc
@@ -15,18 +15,27 @@
 #include "components/feature_engagement/public/feature_list.h"
 #include "components/prefs/pref_service.h"
 #include "components/saved_tab_groups/features.h"
+#include "components/saved_tab_groups/tab_group_sync_service.h"
 #include "components/user_education/views/help_bubble_view.h"
 #include "content/public/test/browser_test.h"
 #include "ui/base/interaction/interactive_test.h"
 
-class SavedTabGroupV2PromoTest : public InteractiveFeaturePromoTest {
+class SavedTabGroupV2PromoTest : public InteractiveFeaturePromoTest,
+                                 public testing::WithParamInterface<bool> {
  public:
   SavedTabGroupV2PromoTest()
       : InteractiveFeaturePromoTest(UseDefaultTrackerAllowingPromos(
             {feature_engagement::kIPHTabGroupsSaveV2CloseGroupFeature})) {
-    feature_list_.InitWithFeatures(
-        {{tab_groups::kTabGroupsSaveV2, tab_groups::kTabGroupsSaveUIUpdate}},
-        {});
+    if (GetParam()) {
+      feature_list_.InitWithFeatures(
+          {{tab_groups::kTabGroupSyncServiceDesktopMigration,
+            tab_groups::kTabGroupsSaveV2, tab_groups::kTabGroupsSaveUIUpdate}},
+          {});
+    } else {
+      feature_list_.InitWithFeatures(
+          {{tab_groups::kTabGroupsSaveV2, tab_groups::kTabGroupsSaveUIUpdate}},
+          {});
+    }
   }
 
   ~SavedTabGroupV2PromoTest() override = default;
@@ -34,17 +43,21 @@
   auto TriggerPromo() {
     auto steps = Steps(
         Do([this]() {
+          tab_groups::TabGroupSyncService* service =
+              tab_groups::SavedTabGroupUtils::GetServiceForProfile(
+                  browser()->profile());
+          ASSERT_TRUE(service);
+          service->SetIsInitializedForTesting(true);
+
           chrome::AddTabAt(browser(), GURL(), 0, true);
           chrome::AddTabAt(browser(), GURL(), 1, true);
-
           tab_groups::TabGroupId group_id =
               browser()->tab_strip_model()->AddToNewGroup({0});
 
           tab_groups::SavedTabGroupUtils::RemoveGroupFromTabstrip(browser(),
                                                                   group_id);
         }),
-        WaitForShow(
-            user_education::HelpBubbleView::kHelpBubbleElementIdForTesting));
+        WaitForPromo(feature_engagement::kIPHTabGroupsSaveV2CloseGroupFeature));
     AddDescription(steps, "SaveAndCloseGroup( %s )");
     return steps;
   }
@@ -53,12 +66,12 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(SavedTabGroupV2PromoTest,
+IN_PROC_BROWSER_TEST_P(SavedTabGroupV2PromoTest,
                        TestShowingIPHOnSavedTabGroupBar) {
   // Show the SavedTabGroupBar and the BookmarkBar.
   PrefService* prefs = browser()->profile()->GetPrefs();
-  const bool original_stgb_pref = browser()->profile()->GetPrefs()->GetBoolean(
-      bookmarks::prefs::kShowTabGroupsInBookmarkBar);
+  const bool original_stgb_pref =
+      prefs->GetBoolean(bookmarks::prefs::kShowTabGroupsInBookmarkBar);
   prefs->SetBoolean(bookmarks::prefs::kShowTabGroupsInBookmarkBar, true);
 
   RunTestSequence(
@@ -71,12 +84,12 @@
                     original_stgb_pref);
 }
 
-IN_PROC_BROWSER_TEST_F(SavedTabGroupV2PromoTest,
+IN_PROC_BROWSER_TEST_P(SavedTabGroupV2PromoTest,
                        TestShowingIPHWithoutSavedTabGroupBar) {
   // Show the SavedTabGroupBar and the BookmarkBar.
   PrefService* prefs = browser()->profile()->GetPrefs();
-  const bool original_stgb_pref = browser()->profile()->GetPrefs()->GetBoolean(
-      bookmarks::prefs::kShowTabGroupsInBookmarkBar);
+  const bool original_stgb_pref =
+      prefs->GetBoolean(bookmarks::prefs::kShowTabGroupsInBookmarkBar);
   prefs->SetBoolean(bookmarks::prefs::kShowTabGroupsInBookmarkBar, false);
 
   RunTestSequence(
@@ -88,3 +101,7 @@
   prefs->SetBoolean(bookmarks::prefs::kShowTabGroupsInBookmarkBar,
                     original_stgb_pref);
 }
+
+INSTANTIATE_TEST_SUITE_P(SavedTabGroupV2Promo,
+                         SavedTabGroupV2PromoTest,
+                         testing::Bool());
diff --git a/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog.cc b/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog.cc
index 531c0a46..506dd609 100644
--- a/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog.cc
+++ b/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog.cc
@@ -76,7 +76,7 @@
 
   ui::DialogModel::Builder dialog_builder =
       ui::DialogModel::Builder(std::move(dialog_delegate_unique));
-  dialog_builder.SetInternalName(kExtensionSettingsOverridenDialogName)
+  dialog_builder.SetInternalName(kExtensionSettingsOverriddenDialogName)
       .SetTitle(show_params.dialog_title)
       .AddParagraph(ui::DialogModelLabel(show_params.message))
       .AddOkButton(
diff --git a/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog.h b/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog.h
index a069da8..afb4f93 100644
--- a/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog.h
+++ b/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_EXTENSIONS_DIALOGS_SETTINGS_OVERRIDDEN_DIALOG_H_
 #define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_DIALOGS_SETTINGS_OVERRIDDEN_DIALOG_H_
 
-static constexpr char kExtensionSettingsOverridenDialogName[] =
-    "ExtensionSettingsOverridenDialog";
+static constexpr char kExtensionSettingsOverriddenDialogName[] =
+    "ExtensionSettingsOverriddenDialog";
 
 #endif  // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_DIALOGS_SETTINGS_OVERRIDDEN_DIALOG_H_
diff --git a/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog_browsertest.cc b/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog_browsertest.cc
index e4f50379..455bd91 100644
--- a/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog_browsertest.cc
@@ -113,8 +113,9 @@
       params.icon = &vector_icons::kProductIcon;
     }
 
-    views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
-                                         kExtensionSettingsOverridenDialogName);
+    views::NamedWidgetShownWaiter waiter(
+        views::test::AnyWidgetTestPasskey{},
+        kExtensionSettingsOverriddenDialogName);
     extensions::ShowSettingsOverriddenDialog(
         std::make_unique<TestDialogController>(std::move(params),
                                                &dialog_result_),
diff --git a/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog_unittest.cc b/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog_unittest.cc
index 35038eb1..b74f9a5 100644
--- a/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog_unittest.cc
+++ b/chrome/browser/ui/views/extensions/dialogs/settings_overridden_dialog_unittest.cc
@@ -65,8 +65,9 @@
     auto controller = std::make_unique<TestDialogController>(state);
     EXPECT_FALSE(state->shown);
 
-    views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
-                                         kExtensionSettingsOverridenDialogName);
+    views::NamedWidgetShownWaiter waiter(
+        views::test::AnyWidgetTestPasskey{},
+        kExtensionSettingsOverriddenDialogName);
     extensions::ShowSettingsOverriddenDialog(std::move(controller),
                                              browser_view()->browser());
     views::Widget* dialog = waiter.WaitIfNeededAndGet();
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
index 700bfe4..c8491d3 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
@@ -305,8 +305,6 @@
       DISTANCE_EXTENSIONS_MENU_BUTTON_ICON_SMALL_SIZE);
   const int icon_label_spacing =
       provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL);
-  const int menu_item_vertical_spacing =
-      provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_VERTICAL_SMALL);
   const int horizontal_spacing =
       provider->GetDistanceMetric(DISTANCE_RELATED_LABEL_HORIZONTAL_LIST);
 
@@ -323,10 +321,6 @@
           views::Builder<views::FlexLayoutView>()
               .SetOrientation(views::LayoutOrientation::kHorizontal)
               .SetIgnoreDefaultMainAxisMargins(true)
-              // Spacing between menu items is done by setting the top margin.
-              // Horizontal margins are added by the parent view.
-              .SetInteriorMargin(
-                  gfx::Insets::TLBR(menu_item_vertical_spacing, 0, 0, 0))
               .AddChildren(
                   // Primary action button.
                   views::Builder<ExtensionsMenuButton>(
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
index c20685f..9a08b9d 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_main_page_view.cc
@@ -58,6 +58,9 @@
 
 namespace {
 
+// Radius for the containers in the extensions menu.
+constexpr int kContainerBackgroundRadius = 12;
+
 // Updates the `toggle_button` text based on its state.
 std::u16string GetSiteSettingToggleText(bool is_on) {
   int label_id = is_on ? IDS_EXTENSIONS_MENU_SITE_SETTINGS_TOGGLE_ON_TOOLTIP
@@ -106,7 +109,10 @@
     kRequestsAccessContainer
   };
 
-  MessageSection(base::RepeatingCallback<void()> reload_callback,
+  MessageSection(int vertical_margin,
+                 int horizontal_margin,
+                 int control_vertical_margin,
+                 base::RepeatingCallback<void()> reload_callback,
                  base::RepeatingCallback<void(const extensions::ExtensionId&)>
                      allow_callback,
                  base::RepeatingCallback<void(const extensions::ExtensionId&)>
@@ -198,6 +204,9 @@
 DEFINE_VIEW_BUILDER(/* No Export */, MessageSection)
 
 MessageSection::MessageSection(
+    int vertical_margin,
+    int horizontal_margin,
+    int control_vertical_margin,
     base::RepeatingCallback<void()> reload_callback,
     base::RepeatingCallback<void(const extensions::ExtensionId&)>
         allow_callback,
@@ -207,21 +216,13 @@
       allow_callback_(std::move(allow_callback)),
       dismiss_callback_(std::move(dismiss_callback)) {
   auto* layout_provider = ChromeLayoutProvider::Get();
-  const int section_vertical_margin = layout_provider->GetDistanceMetric(
-      DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE);
-  const int section_horizontal_margin = layout_provider->GetDistanceMetric(
-      DISTANCE_UNRELATED_CONTROL_HORIZONTAL_LARGE);
-  const int control_vertical_margin = layout_provider->GetDistanceMetric(
-      DISTANCE_RELATED_CONTROL_VERTICAL_SMALL);
 
   views::Builder<MessageSection>(this)
       .SetOrientation(views::BoxLayout::Orientation::kVertical)
-      // TODO(crbug.com/40879945): After adding margins, compute radius from a
-      // variable or create a const variable.
-      .SetBackground(views::CreateThemedRoundedRectBackground(
-          kColorExtensionsMenuContainerBackground, 4))
       .SetInsideBorderInsets(
-          gfx::Insets::VH(section_vertical_margin, section_horizontal_margin))
+          gfx::Insets::VH(vertical_margin, horizontal_margin))
+      .SetBackground(views::CreateThemedRoundedRectBackground(
+          kColorExtensionsMenuContainerBackground, kContainerBackgroundRadius))
       .AddChildren(
           // Text container.
           views::Builder<views::FlexLayoutView>()
@@ -534,8 +535,15 @@
 
   ChromeLayoutProvider* const chrome_layout_provider =
       ChromeLayoutProvider::Get();
-  const int vertical_spacing = chrome_layout_provider->GetDistanceMetric(
-      DISTANCE_RELATED_CONTROL_VERTICAL_SMALL);
+  const int container_vertical_margin =
+      chrome_layout_provider->GetDistanceMetric(
+          DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE);
+  const int container_horizontal_margin =
+      chrome_layout_provider->GetDistanceMetric(
+          DISTANCE_UNRELATED_CONTROL_HORIZONTAL_LARGE);
+  const int control_vertical_spacing =
+      chrome_layout_provider->GetDistanceMetric(
+          DISTANCE_RELATED_CONTROL_VERTICAL_SMALL);
   // This value must be the same as the `HoverButton` vertical margin.
   const int hover_button_vertical_spacing =
       chrome_layout_provider->GetDistanceMetric(
@@ -546,10 +554,6 @@
   const gfx::Insets dialog_insets =
       layout_provider->GetInsetsMetric(views::InsetsMetric::INSETS_DIALOG);
 
-  // Views that need configuration after construction (e.g access size after a
-  // separate view is constructed).
-  views::Label* subheader_title;
-
   views::Builder<ExtensionsMenuMainPageView>(this)
       // Last item is a hover button, so we need to account for the extra
       // vertical spacing. We cannot add horizontal margins at this level
@@ -562,21 +566,33 @@
               dialog_insets.top(), 0,
               dialog_insets.bottom() - hover_button_vertical_spacing, 0)))
       .AddChildren(
-          // Title.
-          views::Builder<views::Label>()
-              .CopyAddressTo(&subheader_title)
-              .SetText(l10n_util::GetStringUTF16(IDS_EXTENSIONS_MENU_TITLE))
-              .SetHorizontalAlignment(gfx::ALIGN_LEFT)
-              .SetTextContext(views::style::CONTEXT_DIALOG_TITLE)
-              .SetTextStyle(views::style::STYLE_HEADLINE_4)
-              .SetEnabledColorId(kColorExtensionsMenuText)
+          // Header.
+          views::Builder<views::FlexLayoutView>()
               .SetProperty(views::kMarginsKey,
-                           gfx::Insets::VH(0, dialog_insets.left())),
+                           gfx::Insets::VH(0, dialog_insets.left()))
+              .AddChildren(
+                  // Title.
+                  views::Builder<views::Label>()
+                      .SetText(
+                          l10n_util::GetStringUTF16(IDS_EXTENSIONS_MENU_TITLE))
+                      .SetHorizontalAlignment(gfx::ALIGN_LEFT)
+                      .SetTextContext(views::style::CONTEXT_DIALOG_TITLE)
+                      .SetTextStyle(views::style::STYLE_HEADLINE_4)
+                      .SetEnabledColorId(kColorExtensionsMenuText)
+                      .SetProperty(views::kFlexBehaviorKey,
+                                   stretch_specification),
+                  // Close button.
+                  views::Builder<views::Button>(
+                      views::BubbleFrameView::CreateCloseButton(
+                          base::BindRepeating(
+                              &ExtensionsMenuHandler::CloseBubble,
+                              base::Unretained(menu_handler_))))),
           // Site settings.
           views::Builder<views::FlexLayoutView>()
               .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
               .SetProperty(views::kMarginsKey,
-                           gfx::Insets::VH(0, dialog_insets.left()))
+                           gfx::Insets::VH(control_vertical_spacing,
+                                           dialog_insets.left()))
               .AddChildren(
                   views::Builder<views::Label>()
                       .CopyAddressTo(&site_settings_label_)
@@ -604,7 +620,7 @@
               .SetHorizontalScrollBarMode(
                   views::ScrollView::ScrollBarMode::kDisabled)
               .SetProperty(views::kMarginsKey,
-                           gfx::Insets::VH(vertical_spacing, 0))
+                           gfx::Insets::VH(control_vertical_spacing, 0))
               .SetContents(
                   views::Builder<views::BoxLayoutView>()
                       .SetOrientation(views::BoxLayout::Orientation::kVertical)
@@ -617,6 +633,9 @@
                           // Message section.
                           views::Builder<MessageSection>(
                               std::make_unique<MessageSection>(
+                                  container_vertical_margin,
+                                  container_horizontal_margin,
+                                  control_vertical_spacing,
                                   base::BindRepeating(
                                       &ExtensionsMenuHandler::
                                           OnReloadPageButtonClicked,
@@ -634,7 +653,18 @@
                           views::Builder<views::BoxLayoutView>()
                               .CopyAddressTo(&menu_items_)
                               .SetOrientation(
-                                  views::BoxLayout::Orientation::kVertical))),
+                                  views::BoxLayout::Orientation::kVertical)
+                              .SetInsideBorderInsets(
+                                  gfx::Insets::VH(container_vertical_margin,
+                                                  container_horizontal_margin))
+                              .SetBackground(
+                                  views::CreateThemedRoundedRectBackground(
+                                      kColorExtensionsMenuContainerBackground,
+                                      kContainerBackgroundRadius))
+                              .SetProperty(
+                                  views::kMarginsKey,
+                                  gfx::Insets::TLBR(control_vertical_spacing, 0,
+                                                    0, 0)))),
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
           // Webstore button.
           views::Builder<HoverButton>(std::make_unique<HoverButton>(
@@ -698,6 +728,18 @@
                           base::Unretained(menu_handler_), extension_id));
   item->Update(site_access_toggle_state, site_permissions_button_state,
                site_permissions_button_access, is_enterprise);
+
+  // Add vertical spacing in between menu items.
+  if (index > 0) {
+    ChromeLayoutProvider* const chrome_layout_provider =
+        ChromeLayoutProvider::Get();
+    const int control_vertical_spacing =
+        chrome_layout_provider->GetDistanceMetric(
+            DISTANCE_RELATED_CONTROL_VERTICAL_SMALL);
+    item->SetInteriorMargin(
+        gfx::Insets::TLBR(control_vertical_spacing, 0, 0, 0));
+  }
+
   menu_items_->AddChildViewAt(std::move(item), index);
 }
 
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
index 75fb4bc..4518b9d7 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
@@ -178,7 +178,7 @@
           DISTANCE_CONTROL_LIST_VERTICAL) /
       2;
 
-  // Views that need confirgutation after construction (e.g access size after a
+  // Views that need configuration after construction (e.g access size after a
   // separate view is constructed).
   views::Label* toggle_label;
 
@@ -244,17 +244,14 @@
       .SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kVertical))
       .AddChildren(
-          // Subheader.
+          // Header.
           views::Builder<views::FlexLayoutView>()
-              .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
               // Add top dialog margins, since its the first element, and
               // horizontal dialog margins. Vertical margins will be handled
               // in between the contents.
               .SetInteriorMargin(gfx::Insets::TLBR(dialog_insets.top(),
                                                    dialog_insets.left(), 0,
                                                    dialog_insets.right()))
-              .SetProperty(views::kBoxLayoutFlexKey,
-                           views::BoxLayoutFlexSpecification())
               .AddChildren(
                   // Back button.
                   views::Builder<views::ImageButton>(
@@ -290,7 +287,13 @@
                                   kColorExtensionsMenuSecondaryText)
                               .SetProperty(views::kMarginsKey,
                                            gfx::Insets::TLBR(
-                                               0, horizontal_spacing, 0, 0)))),
+                                               0, horizontal_spacing, 0, 0))),
+                  // Close button.
+                  views::Builder<views::Button>(
+                      views::BubbleFrameView::CreateCloseButton(
+                          base::BindRepeating(
+                              &ExtensionsMenuHandler::CloseBubble,
+                              base::Unretained(menu_handler))))),
           create_separator_builder(/*full_width=*/true,
                                    /*is_bottom_hover_button=*/false),
           // Content.
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index a1dcdba..c3241e4 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -2671,7 +2671,7 @@
     for (auto* view : std::initializer_list<views::View*>{
              toolbar_->app_menu_button(), GetLocationBarView(),
              toolbar_button_provider_->GetAvatarToolbarButton(),
-             toolbar_button_provider_->GetDownloadButton()}) {
+             toolbar_button_provider_->GetDownloadButton(), top_container_}) {
       if (view) {
         if (auto* dialog = view->GetProperty(views::kAnchoredDialogKey);
             dialog && !user_education::HelpBubbleView::IsHelpBubble(dialog)) {
diff --git a/chrome/browser/ui/views/frame/system_menu_model_builder_browsertest_chromeos.cc b/chrome/browser/ui/views/frame/system_menu_model_builder_browsertest_chromeos.cc
index aa1f585..e9a0ead 100644
--- a/chrome/browser/ui/views/frame/system_menu_model_builder_browsertest_chromeos.cc
+++ b/chrome/browser/ui/views/frame/system_menu_model_builder_browsertest_chromeos.cc
@@ -6,9 +6,9 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
index 2cea77e..e7660352 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -494,8 +494,14 @@
   if (tab_search_container_) {
     tab_search_container_->tab_search_button()->SetBorder(
         views::CreateEmptyBorder(border_insets));
-    if (tab_search_container_->tab_organization_button()) {
-      tab_search_container_->tab_organization_button()->SetBorder(
+
+    if (tab_search_container_->auto_tab_group_button()) {
+      tab_search_container_->auto_tab_group_button()->SetBorder(
+          views::CreateEmptyBorder(border_insets));
+    }
+
+    if (tab_search_container_->tab_declutter_button()) {
+      tab_search_container_->tab_declutter_button()->SetBorder(
           views::CreateEmptyBorder(border_insets));
     }
   }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 075c727..32ee92a7 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -189,7 +189,8 @@
       std::make_unique<views::FlexLayout>());
   suggestion_and_buttons->SetProperty(
       views::kFlexBehaviorKey,
-      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+      views::FlexSpecification(views::LayoutOrientation::kHorizontal,
+                               views::MinimumFlexSizeRule::kScaleToZero,
                                views::MaximumFlexSizeRule::kUnbounded));
 
   suggestion_view_ = suggestion_and_buttons->AddChildView(
diff --git a/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc b/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
index a5ad5a0..4678c9f 100644
--- a/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
+++ b/chrome/browser/ui/views/profiles/first_run_interactive_uitest.cc
@@ -563,7 +563,9 @@
 }
 #endif
 
-IN_PROC_BROWSER_TEST_P(FirstRunParameterizedInteractiveUiTest, SignInAndSync) {
+// TODO(crbug.com/366082752): Re-enable this test
+IN_PROC_BROWSER_TEST_P(FirstRunParameterizedInteractiveUiTest,
+                       DISABLED_SignInAndSync) {
   if (SyncButtonsFeatureConfig() ==
       SyncButtonsFeatureConfig::kButtonsStillLoading) {
     GTEST_SKIP() << "Sync not possible until buttons stop loading";
@@ -712,7 +714,9 @@
       ProfilePicker::FirstRunExitStatus::kCompleted, 1);
 }
 
-IN_PROC_BROWSER_TEST_P(FirstRunParameterizedInteractiveUiTest, DeclineSync) {
+// TODO(crbug.com/366082752): Re-enable this test
+IN_PROC_BROWSER_TEST_P(FirstRunParameterizedInteractiveUiTest,
+                       DISABLED_DeclineSync) {
   if (SyncButtonsFeatureConfig() ==
       SyncButtonsFeatureConfig::kButtonsStillLoading) {
     GTEST_SKIP() << "Decline is not possible until buttons stop loading";
diff --git a/chrome/browser/ui/views/profiles/managed_user_profile_notice_ui_browsertest.cc b/chrome/browser/ui/views/profiles/managed_user_profile_notice_ui_browsertest.cc
index c5b4cbe..0b1395d 100644
--- a/chrome/browser/ui/views/profiles/managed_user_profile_notice_ui_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/managed_user_profile_notice_ui_browsertest.cc
@@ -212,6 +212,8 @@
  public:
   ManagedUserNoticeUIDialogPixelTest()
       : ProfilesPixelTestBaseT<DialogBrowserTest>(GetParam().pixel_test_param) {
+    feature_list_.InitAndDisableFeature(
+        features::kEnterpriseUpdatedProfileCreationScreen);
   }
 
   ~ManagedUserNoticeUIDialogPixelTest() override = default;
@@ -247,6 +249,9 @@
     widget_waiter.WaitIfNeededAndGet();
     observer.Wait();
   }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_P(ManagedUserNoticeUIDialogPixelTest,
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 2ae50a1..86332f0 100644
--- a/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_management_flow_controller.cc
@@ -148,3 +148,12 @@
 ProfileManagementFlowController::GetSignedOutFlowWebContents() const {
   return signed_out_flow_web_contents_.get();
 }
+
+void ProfileManagementFlowController::Reset() {
+  Step previous_step = current_step_;
+
+  // Activate the initial step.
+  SwitchToStep(Step::kProfilePicker, /*reset_state=*/true);
+  // Unregister the previous active step.
+  UnregisterStep(previous_step);
+}
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 d9a64a2..3bd1fad 100644
--- a/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
@@ -99,6 +99,11 @@
   // screen (if the original EntryPoint was to open the picker).
   virtual void CancelPostSignInFlow() = 0;
 
+  // Clears the current state and reset it to the initial state that shows the
+  // main screen. When calling this function the state should not be the
+  // initial one.
+  void Reset();
+
   // Returns a string to use as title for the window, for accessibility
   // purposes. It is used in case the host is not able to obtain a title from
   // the content it's rendering. As a final fallback, if this value is empty
diff --git a/chrome/browser/ui/views/profiles/profile_picker_dice_reauth_provider.cc b/chrome/browser/ui/views/profiles/profile_picker_dice_reauth_provider.cc
index ac3bdc3..9a8ae654 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_dice_reauth_provider.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_dice_reauth_provider.cc
@@ -52,17 +52,18 @@
                    {.email = email_to_reauth, .continue_url = continue_url});
 }
 
-ReauthUIError ComputeReauthUIError(ProfilePickerReauthResult result) {
+ForceSigninUIError ComputeReauthUIError(ProfilePickerReauthResult result,
+                                        const std::string& reauth_email) {
   switch (result) {
     case ProfilePickerReauthResult::kSuccess:
     case ProfilePickerReauthResult::kSuccessTokenAlreadyValid:
-      return ReauthUIError::kNone;
+      return ForceSigninUIError::ErrorNone();
     case ProfilePickerReauthResult::kErrorUsedNewEmail:
     case ProfilePickerReauthResult::kErrorUsedOtherSignedInEmail:
-      return ReauthUIError::kWrongAccount;
+      return ForceSigninUIError::ReauthWrongAccount(reauth_email);
     case ProfilePickerReauthResult::kTimeoutForceSigninVerifierCheck:
     case ProfilePickerReauthResult::kTimeoutSigninError:
-      return ReauthUIError::kTimeout;
+      return ForceSigninUIError::ReauthTimeout();
   }
 }
 
@@ -73,7 +74,8 @@
     Profile* profile,
     const std::string& gaia_id_to_reauth,
     const std::string& email_to_reauth,
-    base::OnceCallback<void(bool, ReauthUIError)> on_reauth_completed)
+    base::OnceCallback<void(bool, const ForceSigninUIError&)>
+        on_reauth_completed)
     : host_(*host),
       profile_(*profile),
       identity_manager_(*IdentityManagerFactory::GetForProfile(profile)),
@@ -263,6 +265,6 @@
   // Hide the toolbar in case it was visible after showing the reauth page.
   host_->SetNativeToolbarVisible(false);
 
-  ReauthUIError error = ComputeReauthUIError(result);
+  ForceSigninUIError error = ComputeReauthUIError(result, email_to_reauth_);
   std::move(on_reauth_completed_).Run(success, error);
 }
diff --git a/chrome/browser/ui/views/profiles/profile_picker_dice_reauth_provider.h b/chrome/browser/ui/views/profiles/profile_picker_dice_reauth_provider.h
index 095d0ea..a95364b 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_dice_reauth_provider.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_dice_reauth_provider.h
@@ -56,7 +56,8 @@
       Profile* profile,
       const std::string& email_to_reauth,
       const std::string& gaia_id_to_reauth,
-      base::OnceCallback<void(bool, ReauthUIError)> on_reauth_completed);
+      base::OnceCallback<void(bool, const ForceSigninUIError&)>
+          on_reauth_completed);
   ~ProfilePickerDiceReauthProvider() override;
 
   ProfilePickerDiceReauthProvider(const ProfilePickerDiceReauthProvider&) =
@@ -111,7 +112,8 @@
   raw_ref<signin::IdentityManager> identity_manager_;
   const std::string gaia_id_to_reauth_;
   const std::string email_to_reauth_;
-  base::OnceCallback<void(bool, ReauthUIError)> on_reauth_completed_;
+  base::OnceCallback<void(bool, const ForceSigninUIError&)>
+      on_reauth_completed_;
 
   // Prevent `profile_` from being destroyed first.
   std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_;
diff --git a/chrome/browser/ui/views/profiles/profile_picker_dice_sign_in_provider_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_dice_sign_in_provider_browsertest.cc
index 4a8d07c9..0eb6fa4 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_dice_sign_in_provider_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_dice_sign_in_provider_browsertest.cc
@@ -52,6 +52,7 @@
   MOCK_METHOD(web_modal::WebContentsModalDialogHost*,
               GetWebContentsModalDialogHost,
               ());
+  MOCK_METHOD(void, Reset, ());
 };
 
 Profile* GetContentsProfile(content::WebContents* contents) {
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 7fa0be4..5a7f74c4 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
@@ -312,7 +312,8 @@
 std::unique_ptr<ProfileManagementStepController> CreateReauthtep(
     ProfilePickerWebContentsHost* host,
     Profile* profile,
-    base::OnceCallback<void(bool, ReauthUIError)> on_reauth_completed) {
+    base::OnceCallback<void(bool, const ForceSigninUIError&)>
+        on_reauth_completed) {
   ProfileAttributesEntry* entry =
       g_browser_process->profile_manager()
           ->GetProfileAttributesStorage()
@@ -374,7 +375,7 @@
 
 void ProfilePickerFlowController::SwitchToReauth(
     Profile* profile,
-    base::OnceCallback<void(ReauthUIError)> on_error_callback) {
+    base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback) {
   DCHECK_EQ(Step::kProfilePicker, current_step());
 
   // if the step was already initialized, unregister to make sure the new
@@ -403,11 +404,11 @@
 
 void ProfilePickerFlowController::OnReauthCompleted(
     Profile* profile,
-    base::OnceCallback<void(ReauthUIError)> on_error_callback,
+    base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback,
     bool success,
-    ReauthUIError error) {
+    const ForceSigninUIError& error) {
   if (!success) {
-    CHECK_NE(error, ReauthUIError::kNone);
+    CHECK_NE(error.type(), ForceSigninUIError::Type::kNone);
 
     SwitchToStep(
         Step::kProfilePicker, /*reset_state=*/true,
@@ -426,8 +427,8 @@
 }
 
 void ProfilePickerFlowController::OnProfilePickerStepShownReauthError(
-    base::OnceCallback<void(ReauthUIError)> on_error_callback,
-    ReauthUIError error,
+    base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback,
+    const ForceSigninUIError& error,
     bool switch_step_success) {
   // If the step switch to the profile picker was not successful, do not proceed
   // with displaying the error dialog.
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 4106e88..6c49cdf8 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h
@@ -17,7 +17,7 @@
 struct CoreAccountInfo;
 class Profile;
 class ProfilePickerSignedInFlowController;
-enum class ReauthUIError;
+class ForceSigninUIError;
 
 class ProfilePickerFlowController : public ProfileManagementFlowControllerImpl {
  public:
@@ -34,7 +34,7 @@
 
   void SwitchToReauth(
       Profile* profile,
-      base::OnceCallback<void(ReauthUIError)> on_error_callback);
+      base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback);
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -66,13 +66,13 @@
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   void OnReauthCompleted(
       Profile* profile,
-      base::OnceCallback<void(ReauthUIError)> on_error_callback,
+      base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback,
       bool success,
-      ReauthUIError error);
+      const ForceSigninUIError& error);
 
   void OnProfilePickerStepShownReauthError(
-      base::OnceCallback<void(ReauthUIError)> on_error_callback,
-      ReauthUIError error,
+      base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback,
+      const ForceSigninUIError& error,
       bool switch_step_success);
 #endif
 
diff --git a/chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.cc
index 4035938..7610a4c 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.cc
@@ -168,6 +168,13 @@
       GURL(chrome::kChromeUIProfilePickerUrl).Resolve("profile-switch"));
 }
 
+void ProfilePickerSignedInFlowController::ResetHost() {
+  CHECK(IsInitialized());
+
+  Cancel();
+  host_->Reset();
+}
+
 std::optional<SkColor> ProfilePickerSignedInFlowController::GetProfileColor()
     const {
   // The new profile theme may be overridden by an existing policy theme. This
diff --git a/chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.h b/chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.h
index 6d42052..9624991 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_signed_in_flow_controller.h
@@ -60,6 +60,9 @@
   // By default does not do anything, in the flow it will be as if the dialog
   // was closed.
   virtual void Cancel();
+  // Resets the host by redirecting to the main profile picker screen and
+  // canceling the ongoing signed in flow.
+  void ResetHost();
 
   // Finishes the creation flow for `profile_`: marks it fully created,
   // transitions from `host_` to a new browser window and calls `callback` if
diff --git a/chrome/browser/ui/views/profiles/profile_picker_turn_sync_on_delegate.cc b/chrome/browser/ui/views/profiles/profile_picker_turn_sync_on_delegate.cc
index d047f3ac7..bb9eebd 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_turn_sync_on_delegate.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_turn_sync_on_delegate.cc
@@ -88,11 +88,15 @@
 void ProfilePickerTurnSyncOnDelegate::ShowLoginError(
     const SigninUIError& error) {
   LogOutcome(ProfileMetrics::ProfileSignedInFlowOutcome::kLoginError);
+
+  // If the controller is null we cannot treat the error.
+  if (!controller_) {
+    return;
+  }
+
   if (IsLacrosPrimaryProfileFirstRun(profile_)) {
     // The primary profile onboarding is silently skipped if there's any error.
-    if (controller_) {
-      controller_->FinishAndOpenBrowser(PostHostClearedCallback());
-    }
+    controller_->FinishAndOpenBrowser(PostHostClearedCallback());
     return;
   }
 
@@ -101,17 +105,26 @@
   // profile.
   if (error.type() ==
       SigninUIError::Type::kAccountAlreadyUsedByAnotherProfile) {
-    if (controller_) {
-      controller_->SwitchToProfileSwitch(error.another_profile_path());
-    }
+    controller_->SwitchToProfileSwitch(error.another_profile_path());
+    return;
+  }
+
+  // Abort the flow completely and reset the host in case of ForceSignin if the
+  // user is not allowed to sign in by policy with this account. In
+  // non-ForceSignin, the user can still browse and be signed in but cannot
+  // enable sync.
+  if (signin_util::IsForceSigninEnabled() &&
+      error.type() ==
+          SigninUIError::Type::kUsernameNotAllowedByPatternFromPrefs) {
+    controller_->ResetHost();
+    // TODO(b/360733721): Should also open a dialog showing an error after
+    // navigating back to the Profile Picker main page.
     return;
   }
 
   // Open the browser and when it's done, show the login error.
-  if (controller_) {
-    controller_->FinishAndOpenBrowser(PostHostClearedCallback(base::BindOnce(
-        &TurnSyncOnHelper::Delegate::ShowLoginErrorForBrowser, error)));
-  }
+  controller_->FinishAndOpenBrowser(PostHostClearedCallback(base::BindOnce(
+      &TurnSyncOnHelper::Delegate::ShowLoginErrorForBrowser, error)));
 }
 
 void ProfilePickerTurnSyncOnDelegate::ShowMergeSyncDataConfirmation(
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index 77a5cd8..e41faa7 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -221,7 +221,7 @@
 // static
 void ProfilePicker::SwitchToReauth(
     Profile* profile,
-    base::OnceCallback<void(ReauthUIError)> on_error_callback) {
+    base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback) {
   if (g_profile_picker_view) {
     g_profile_picker_view->SwitchToReauth(profile,
                                           std::move(on_error_callback));
@@ -545,6 +545,10 @@
   return this;
 }
 
+void ProfilePickerView::Reset() {
+  flow_controller_->Reset();
+}
+
 void ProfilePickerView::SwitchToSignedOutPostIdentityFlow(
     std::optional<SkColor> profile_color,
     base::TimeTicks profile_picked_time_on_startup,
@@ -892,7 +896,7 @@
 
 void ProfilePickerView::SwitchToReauth(
     Profile* profile,
-    base::OnceCallback<void(ReauthUIError)> on_error_callback) {
+    base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback) {
   GetProfilePickerFlowController()->SwitchToReauth(
       profile, std::move(on_error_callback));
 }
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.h b/chrome/browser/ui/views/profiles/profile_picker_view.h
index 6ffce7b..bad613cc 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.h
@@ -36,6 +36,7 @@
 class ProfilePickerFlowController;
 class Browser;
 class ProfilePickerFeaturePromoController;
+class ForceSigninUIError;
 
 namespace content {
 struct ContextMenuParams;
@@ -79,6 +80,7 @@
   web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()
       override;
   content::WebContentsDelegate* GetWebContentsDelegate() override;
+  void Reset() override;
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   void SetNativeToolbarVisible(bool visible) override;
@@ -238,7 +240,7 @@
   // `on_error_callback`.
   void SwitchToReauth(
       Profile* profile,
-      base::OnceCallback<void(ReauthUIError)> on_error_callback);
+      base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback);
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
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 20a6205..1fe2d2d 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -13,6 +13,8 @@
 #include "base/json/values_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
@@ -92,10 +94,12 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_metrics.h"
+#include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "components/signin/public/identity_manager/identity_utils.h"
 #include "components/signin/public/identity_manager/primary_account_mutator.h"
 #include "components/sync/base/pref_names.h"
 #include "components/sync/service/sync_service.h"
@@ -872,6 +876,43 @@
   }
 
   bool IsForceSigninErrorDialogShown() {
+    CheckMainProfilePickerUrlOpened();
+    return content::EvalJs(web_contents(),
+                           // Check the `open` field
+                           base::StrCat({kForceSigninErrorDialogPath, ".open"}))
+        .ExtractBool();
+  }
+
+  std::u16string GetForceSigninErrorDialogTitleText() {
+    CheckMainProfilePickerUrlOpened();
+    return std::u16string(base::TrimWhitespace(
+        base::UTF8ToUTF16(
+            content::EvalJs(
+                web_contents(),
+                // Get the title text content of the dialog.
+                base::StrCat({kForceSigninErrorDialogPath,
+                              ".querySelector(\'#dialog-title\').textContent"}))
+                .ExtractString()),
+        base::TRIM_ALL));
+  }
+
+  std::u16string GetForceSigninErrorDialogBodyText() {
+    CheckMainProfilePickerUrlOpened();
+    return std::u16string(base::TrimWhitespace(
+        base::UTF8ToUTF16(
+            content::EvalJs(
+                web_contents(),
+                // Get the body text content of the dialog.
+                base::StrCat({kForceSigninErrorDialogPath,
+                              ".querySelector(\'#dialog-body\').textContent"}))
+                .ExtractString()),
+        base::TRIM_ALL));
+  }
+
+  base::HistogramTester* histogram_tester() { return &histogram_tester_; }
+
+ private:
+  void CheckMainProfilePickerUrlOpened() {
     // Make sure the profile picker is opened, with the main profile picker view
     // (where the dialog can be shown), and the page is fully loaded.
     EXPECT_TRUE(ProfilePicker::IsOpen());
@@ -879,21 +920,14 @@
     EXPECT_EQ(web_contents()->GetURL().GetWithEmptyPath(),
               main_profile_picker_url);
     WaitForLoadStop(main_profile_picker_url);
-
-    return content::EvalJs(
-               web_contents(),
-               // Get down to the `forceSigninErrorDialog` cr-dialog node and
-               // check the `open` field.
-               "document.body.getElementsByTagName('profile-picker-app')[0]."
-               "shadowRoot.getElementById('mainView').shadowRoot."
-               "getElementById(\""
-               "forceSigninErrorDialog\").open")
-        .ExtractBool();
   }
 
-  base::HistogramTester* histogram_tester() { return &histogram_tester_; }
+  // 'forceSigninErrorDialog' cr-dialog node.
+  static constexpr char kForceSigninErrorDialogPath[] =
+      "document.body.getElementsByTagName('profile-picker-app')[0]."
+      "shadowRoot.getElementById('mainView').shadowRoot."
+      "getElementById(\'forceSigninErrorDialog\')";
 
- private:
   signin_util::ScopedForceSigninSetterForTesting force_signin_setter_;
   base::HistogramTester histogram_tester_;
 };
@@ -1101,6 +1135,11 @@
   WaitForLoadStop(GURL("chrome://profile-picker"));
   EXPECT_TRUE(ProfilePicker::IsOpen());
   EXPECT_TRUE(IsForceSigninErrorDialogShown());
+  // Check error dialog content.
+  ForceSigninUIError::UiTexts errors =
+      ForceSigninUIError::ReauthWrongAccount(email).GetErrorTexts();
+  EXPECT_EQ(GetForceSigninErrorDialogTitleText(), errors.first);
+  EXPECT_EQ(GetForceSigninErrorDialogBodyText(), errors.second);
   EXPECT_EQ(BrowserList::GetInstance()->size(), initial_browser_count);
   EXPECT_TRUE(entry->IsSigninRequired());
   histogram_tester()->ExpectUniqueSample(
@@ -1217,6 +1256,50 @@
   EXPECT_NE(default_profile_entry->GetActiveTime(), base::Time());
 }
 
+// Regression tetst for b/360733721.
+IN_PROC_BROWSER_TEST_F(
+    ForceSigninProfilePickerCreationFlowBrowserTest,
+    ForceSigninWithPatternMatchingShouldFailSigninWithWrongPatternEmail) {
+  // Set the username pattern restriction.
+  PrefService* local_state = g_browser_process->local_state();
+  local_state->SetString(prefs::kGoogleServicesUsernamePattern, "*.google.com");
+
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  size_t initial_number_of_profiles = profile_manager->GetNumberOfProfiles();
+
+  ASSERT_TRUE(ProfilePicker::IsOpen());
+
+  GURL initial_picker_url =
+      ProfilePicker::GetWebViewForTesting()->GetWebContents()->GetURL();
+
+  // Start the signin process.
+  Profile* profile_being_created = StartDiceSignIn(true);
+  // Profile will be destroyed at the end of the flow.
+  ProfileDestructionWaiter destruction_waiter(profile_being_created);
+  // During signin process a new profile is created.
+  EXPECT_EQ(profile_manager->GetNumberOfProfiles(),
+            initial_number_of_profiles + 1u);
+
+  // Make sure that the ProfilePicker navigated.
+  EXPECT_NE(initial_picker_url,
+            ProfilePicker::GetWebViewForTesting()->GetWebContents()->GetURL());
+
+  const std::string email = "joe.consumer@gmail.com";
+  // Verify that patternt does not match.
+  ASSERT_FALSE(signin::IsUsernameAllowedByPatternFromPrefs(local_state, email));
+  // Signing in with a profile that does not match the pattern should stop the
+  // profile creation flow.
+  FinishDiceSignIn(profile_being_created, email, "Joe", kNoHostedDomainFound);
+
+  // Returning to the profile picker main page.
+  WaitForLoadStop(GURL("chrome://profile-picker"));
+  // Created profile is destroyed.
+  destruction_waiter.Wait();
+  EXPECT_EQ(profile_manager->GetNumberOfProfiles(), initial_number_of_profiles);
+  // TODO(b/360733721): Should also test that the dialog is opened with an error
+  // shown when implemented.
+}
+
 class ForceSigninProfilePickerCreationFlowBrowserTestWithPRE
     : public ForceSigninProfilePickerCreationFlowBrowserTest {
  public:
@@ -1267,6 +1350,11 @@
   EXPECT_EQ(initial_browser_count, BrowserList::GetInstance()->size());
   // Error dialog is shown on top of the ProfilePicker.
   EXPECT_TRUE(IsForceSigninErrorDialogShown());
+  // Check error dialog content.
+  ForceSigninUIError::UiTexts errors =
+      ForceSigninUIError::ReauthNotAllowed().GetErrorTexts();
+  EXPECT_EQ(GetForceSigninErrorDialogTitleText(), errors.first);
+  EXPECT_EQ(GetForceSigninErrorDialogBodyText(), errors.second);
   // Profile is still locked.
   EXPECT_TRUE(existing_entry->IsSigninRequired());
 }
diff --git a/chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h b/chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h
index 5b659b8..8b0e057 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h
@@ -61,6 +61,9 @@
   virtual web_modal::WebContentsModalDialogHost*
   GetWebContentsModalDialogHost() = 0;
 
+  // Clears the current state an Shows the main screen.
+  virtual void Reset() = 0;
+
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   // Changes the visibility of the host's native toolbar, which shows a back
   // button.
diff --git a/chrome/browser/ui/views/select_file_dialog_extension/BUILD.gn b/chrome/browser/ui/views/select_file_dialog_extension/BUILD.gn
index c614d9f..0b8b49ac 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension/BUILD.gn
+++ b/chrome/browser/ui/views/select_file_dialog_extension/BUILD.gn
@@ -21,11 +21,11 @@
     "//chrome/browser/ash/arc/fileapi",
     "//chrome/browser/ash/extensions/file_manager",
     "//chrome/browser/ash/file_manager",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/dlp",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/chromeos/policy/dlp",
     "//chrome/browser/extensions",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/system_web_dialog",
     "//ui/chromeos/styles:cros_tokens_color_mappings_generator",
   ]
diff --git a/chrome/browser/ui/views/select_file_dialog_extension/select_file_dialog_extension.cc b/chrome/browser/ui/views/select_file_dialog_extension/select_file_dialog_extension.cc
index 8872579..9bdf039 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension/select_file_dialog_extension.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension/select_file_dialog_extension.cc
@@ -26,9 +26,6 @@
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/ash/file_manager/select_file_dialog_util.h"
 #include "chrome/browser/ash/file_manager/url_util.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_web_dialog.h"
-#include "chrome/browser/ash/login/ui/webui_login_view.h"
 #include "chrome/browser/ash/policy/dlp/dlp_files_controller_ash.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h"
@@ -37,6 +34,9 @@
 #include "chrome/browser/extensions/extension_view_host.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_web_dialog.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
diff --git a/chrome/browser/ui/views/tabs/tab_organization_button_unittest.cc b/chrome/browser/ui/views/tabs/tab_organization_button_unittest.cc
index 7cc28e3..dc2f96f 100644
--- a/chrome/browser/ui/views/tabs/tab_organization_button_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_organization_button_unittest.cc
@@ -29,7 +29,7 @@
         l10n_util::GetStringUTF16(IDS_TAB_ORGANIZE),
         l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ORGANIZE),
         l10n_util::GetStringUTF16(IDS_ACCNAME_TAB_ORGANIZE),
-        kTabOrganizationButtonElementId, Edge::kRight);
+        kAutoTabGroupButtonElementId, Edge::kRight);
   }
 
   void MockButtonCallback() { button_callback_count_++; }
diff --git a/chrome/browser/ui/views/tabs/tab_search_container.cc b/chrome/browser/ui/views/tabs/tab_search_container.cc
index 1388935..e67ad73 100644
--- a/chrome/browser/ui/views/tabs/tab_search_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_search_container.cc
@@ -4,8 +4,11 @@
 
 #include "chrome/browser/ui/views/tabs/tab_search_container.h"
 
+#include <memory>
+
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
+#include "base/types/pass_key.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/tabs/organization/tab_declutter_controller.h"
@@ -57,6 +60,141 @@
 
 }  // namespace
 
+TabSearchContainer::TabOrganizationAnimationSession::
+    TabOrganizationAnimationSession(
+        TabOrganizationButton* button,
+        TabSearchContainer* container,
+        AnimationSessionType session_type,
+        base::OnceCallback<void()> on_animation_ended)
+    : button_(button),
+      container_(container),
+      expansion_animation_(container),
+      flat_edge_animation_(container),
+      opacity_animation_(container),
+      session_type_(session_type),
+      on_animation_ended_(std::move(on_animation_ended)) {
+  if (session_type_ == AnimationSessionType::HIDE) {
+    expansion_animation_.Reset(1);
+    flat_edge_animation_.Reset(1);
+    opacity_animation_.Reset(1);
+  }
+}
+
+TabSearchContainer::TabOrganizationAnimationSession::
+    ~TabOrganizationAnimationSession() = default;
+
+void TabSearchContainer::TabOrganizationAnimationSession::Start() {
+  if (session_type_ ==
+      TabOrganizationAnimationSession::AnimationSessionType::SHOW) {
+    Show();
+  } else {
+    Hide();
+  }
+}
+
+void TabSearchContainer::TabOrganizationAnimationSession::
+    ResetAnimationForTesting(double value) {
+  if (opacity_animation_delay_timer_.IsRunning()) {
+    opacity_animation_delay_timer_.FireNow();
+  }
+
+  expansion_animation_.Reset(value);
+  flat_edge_animation_.Reset(value);
+  opacity_animation_.Reset(value);
+}
+
+void TabSearchContainer::TabOrganizationAnimationSession::Show() {
+  expansion_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100);
+  opacity_animation_.SetTweenType(gfx::Tween::Type::LINEAR);
+  flat_edge_animation_.SetTweenType(gfx::Tween::Type::LINEAR);
+
+  expansion_animation_.SetSlideDuration(
+      GetAnimationDuration(kExpansionInDuration));
+  flat_edge_animation_.SetSlideDuration(
+      GetAnimationDuration(kFlatEdgeInDuration));
+  opacity_animation_.SetSlideDuration(GetAnimationDuration(kOpacityInDuration));
+
+  expansion_animation_.Show();
+  flat_edge_animation_.Show();
+
+  const base::TimeDelta delay = GetAnimationDuration(kOpacityDelay);
+  opacity_animation_delay_timer_.Start(
+      FROM_HERE, delay, this,
+      &TabSearchContainer::TabOrganizationAnimationSession::
+          ShowOpacityAnimation);
+}
+
+void TabSearchContainer::TabOrganizationAnimationSession::Hide() {
+  // Animate and hide existing chip.
+  if (session_type_ ==
+      TabOrganizationAnimationSession::AnimationSessionType::SHOW) {
+    if (opacity_animation_delay_timer_.IsRunning()) {
+      opacity_animation_delay_timer_.FireNow();
+    }
+    session_type_ = TabOrganizationAnimationSession::AnimationSessionType::HIDE;
+  }
+
+  expansion_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100);
+  opacity_animation_.SetTweenType(gfx::Tween::Type::LINEAR);
+  flat_edge_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100);
+
+  expansion_animation_.SetSlideDuration(
+      GetAnimationDuration(kExpansionOutDuration));
+
+  flat_edge_animation_.SetSlideDuration(
+      GetAnimationDuration(kFlatEdgeOutDuration));
+
+  opacity_animation_.SetSlideDuration(
+      GetAnimationDuration(kOpacityOutDuration));
+
+  expansion_animation_.Hide();
+  flat_edge_animation_.Hide();
+  opacity_animation_.Hide();
+}
+
+base::TimeDelta
+TabSearchContainer::TabOrganizationAnimationSession::GetAnimationDuration(
+    base::TimeDelta duration) {
+  return gfx::Animation::ShouldRenderRichAnimation() ? duration
+                                                     : base::TimeDelta();
+}
+
+void TabSearchContainer::TabOrganizationAnimationSession::
+    ShowOpacityAnimation() {
+  opacity_animation_.Show();
+}
+
+void TabSearchContainer::TabOrganizationAnimationSession::ApplyAnimationValue(
+    const gfx::Animation* animation) {
+  float value = animation->GetCurrentValue();
+  if (animation == &expansion_animation_) {
+    button_->SetWidthFactor(value);
+  } else if (animation == &flat_edge_animation_) {
+    container_->tab_search_button()->SetFlatEdgeFactor(1 - value);
+    button_->SetFlatEdgeFactor(1 - value);
+  } else if (animation == &opacity_animation_) {
+    button_->SetOpacity(value);
+  }
+}
+
+void TabSearchContainer::TabOrganizationAnimationSession::MarkAnimationDone(
+    const gfx::Animation* animation) {
+  if (animation == &expansion_animation_) {
+    expansion_animation_done_ = true;
+  } else if (animation == &flat_edge_animation_) {
+    flat_edge_animation_done_ = true;
+  } else {
+    opacity_animation_done_ = true;
+  }
+
+  if (expansion_animation_done_ && flat_edge_animation_done_ &&
+      opacity_animation_done_) {
+    if (on_animation_ended_) {
+      std::move(on_animation_ended_).Run();
+    }
+  }
+}
+
 TabSearchContainer::TabSearchContainer(
     TabStripController* tab_strip_controller,
     TabStripModel* tab_strip_model,
@@ -91,43 +229,25 @@
       before_tab_strip ? tab_search_button_index + 1 : tab_search_button_index;
   // TODO(crbug.com/40925230): Consider hiding the button when the request has
   // started, vs. when the button as clicked.
-  tab_organization_button_ = AddChildViewAt(
-      std::make_unique<TabOrganizationButton>(
-          tab_strip_controller,
-          base::BindRepeating(&TabSearchContainer::OnOrganizeButtonClicked,
-                              base::Unretained(this)),
-          base::BindRepeating(&TabSearchContainer::OnOrganizeButtonDismissed,
-                              base::Unretained(this)),
-          l10n_util::GetStringUTF16(IDS_TAB_ORGANIZE),
-          l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ORGANIZE),
-          l10n_util::GetStringUTF16(IDS_ACCNAME_TAB_ORGANIZE),
-          kTabOrganizationButtonElementId,
-          GetFlatEdge(false, before_tab_strip)),
-      index);
+  auto_tab_group_button_ = AddChildViewAt(
+      CreateAutoTabGroupButton(tab_strip_controller, before_tab_strip), index);
 
-  tab_organization_button_->SetProperty(views::kCrossAxisAlignmentKey,
-                                        views::LayoutAlignment::kCenter);
-  const int space_between_buttons = 2;
-  gfx::Insets margin = gfx::Insets();
-  if (before_tab_strip) {
-    margin.set_left(space_between_buttons);
-  } else {
-    margin.set_right(space_between_buttons);
-  }
-  tab_organization_button_->SetProperty(views::kMarginsKey, margin);
-  tab_organization_button_->SetOpacity(0);
+  SetupButtonProperties(auto_tab_group_button_, before_tab_strip);
 
   browser_ = tab_strip_controller->GetBrowser();
 
   // `tab_declutter_controller_` will be null for some profile types and if
   // feature is not enabled.
   if (tab_declutter_controller_) {
+    tab_declutter_button_ = AddChildViewAt(
+        CreateTabDeclutterButton(tab_strip_controller, before_tab_strip),
+        index);
+
+    SetupButtonProperties(tab_declutter_button_, before_tab_strip);
+
     tab_declutter_observation_.Observe(tab_declutter_controller_);
   }
 
-  expansion_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100);
-  opacity_animation_.SetTweenType(gfx::Tween::Type::LINEAR);
-
   SetLayoutManager(std::make_unique<views::FlexLayout>());
 }
 
@@ -137,30 +257,87 @@
   }
 }
 
-void TabSearchContainer::ShowTabOrganization() {
+void TabSearchContainer::SetupButtonProperties(TabOrganizationButton* button,
+                                               bool before_tab_strip) {
+  // Set the margins for the button
+  const int space_between_buttons = 2;
+  gfx::Insets margin;
+  if (before_tab_strip) {
+    margin.set_left(space_between_buttons);
+  } else {
+    margin.set_right(space_between_buttons);
+  }
+  button->SetProperty(views::kMarginsKey, margin);
+
+  // Set opacity for the button
+  button->SetOpacity(0);
+}
+
+std::unique_ptr<TabOrganizationButton>
+TabSearchContainer::CreateAutoTabGroupButton(
+    TabStripController* tab_strip_controller,
+    bool before_tab_strip) {
+  auto button = std::make_unique<TabOrganizationButton>(
+      tab_strip_controller,
+      base::BindRepeating(&TabSearchContainer::OnAutoTabGroupButtonClicked,
+                          base::Unretained(this)),
+      base::BindRepeating(&TabSearchContainer::OnAutoTabGroupButtonDismissed,
+                          base::Unretained(this)),
+      l10n_util::GetStringUTF16(IDS_TAB_ORGANIZE),
+      l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ORGANIZE),
+      l10n_util::GetStringUTF16(IDS_ACCNAME_TAB_ORGANIZE),
+      kAutoTabGroupButtonElementId, GetFlatEdge(false, before_tab_strip));
+
+  button->SetProperty(views::kCrossAxisAlignmentKey,
+                      views::LayoutAlignment::kCenter);
+  return button;
+}
+
+std::unique_ptr<TabOrganizationButton>
+TabSearchContainer::CreateTabDeclutterButton(
+    TabStripController* tab_strip_controller,
+    bool before_tab_strip) {
+  auto button = std::make_unique<TabOrganizationButton>(
+      tab_strip_controller,
+      base::BindRepeating(&TabSearchContainer::OnTabDeclutterButtonClicked,
+                          base::Unretained(this)),
+      base::BindRepeating(&TabSearchContainer::OnTabDeclutterButtonDismissed,
+                          base::Unretained(this)),
+      l10n_util::GetStringUTF16(IDS_TAB_DECLUTTER),
+      l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_DECLUTTER),
+      l10n_util::GetStringUTF16(IDS_ACCNAME_TAB_DECLUTTER),
+      kTabDeclutterButtonElementId, GetFlatEdge(false, before_tab_strip));
+
+  button->SetProperty(views::kCrossAxisAlignmentKey,
+                      views::LayoutAlignment::kCenter);
+  return button;
+}
+
+void TabSearchContainer::ShowTabOrganization(TabOrganizationButton* button) {
   if (locked_expansion_view_->IsMouseHovered()) {
-    SetLockedExpansionMode(LockedExpansionMode::kWillShow);
+    SetLockedExpansionMode(LockedExpansionMode::kWillShow, button);
   }
   if (locked_expansion_mode_ == LockedExpansionMode::kNone) {
-    ExecuteShowTabOrganization();
+    ExecuteShowTabOrganization(button);
   }
 }
 
-void TabSearchContainer::HideTabOrganization() {
+void TabSearchContainer::HideTabOrganization(TabOrganizationButton* button) {
   if (locked_expansion_view_->IsMouseHovered()) {
-    SetLockedExpansionMode(LockedExpansionMode::kWillHide);
+    SetLockedExpansionMode(LockedExpansionMode::kWillHide, button);
   }
   if (locked_expansion_mode_ == LockedExpansionMode::kNone) {
-    ExecuteHideTabOrganization();
+    ExecuteHideTabOrganization(button);
   }
 }
 
 void TabSearchContainer::SetLockedExpansionModeForTesting(
-    LockedExpansionMode mode) {
-  SetLockedExpansionMode(mode);
+    LockedExpansionMode mode,
+    TabOrganizationButton* button) {
+  SetLockedExpansionMode(mode, button);
 }
 
-void TabSearchContainer::OnOrganizeButtonClicked() {
+void TabSearchContainer::OnAutoTabGroupButtonClicked() {
   base::UmaHistogramEnumeration(kTriggerOutcomeName, TriggerOutcome::kAccepted);
   tab_organization_service_->OnActionUIAccepted(browser_);
 
@@ -168,10 +345,10 @@
   UMA_HISTOGRAM_BOOLEAN("Tab.Organization.Proactive.Clicked", true);
 
   // Force hide the button when pressed, bypassing locked expansion mode.
-  ExecuteHideTabOrganization();
+  ExecuteHideTabOrganization(auto_tab_group_button_);
 }
 
-void TabSearchContainer::OnOrganizeButtonDismissed() {
+void TabSearchContainer::OnAutoTabGroupButtonDismissed() {
   base::UmaHistogramEnumeration(kTriggerOutcomeName,
                                 TriggerOutcome::kDismissed);
   tab_organization_service_->OnActionUIDismissed(browser_);
@@ -179,35 +356,41 @@
   UMA_HISTOGRAM_BOOLEAN("Tab.Organization.Proactive.Clicked", false);
 
   // Force hide the button when pressed, bypassing locked expansion mode.
-  ExecuteHideTabOrganization();
+  ExecuteHideTabOrganization(auto_tab_group_button_);
 }
 
-void TabSearchContainer::OnOrganizeButtonTimeout() {
-  base::UmaHistogramEnumeration(kTriggerOutcomeName, TriggerOutcome::kTimedOut);
-
-  UMA_HISTOGRAM_BOOLEAN("Tab.Organization.Proactive.Clicked", false);
+void TabSearchContainer::OnOrganizeButtonTimeout(
+    TabOrganizationButton* button) {
+  if (button == auto_tab_group_button_) {
+    base::UmaHistogramEnumeration(kTriggerOutcomeName,
+                                  TriggerOutcome::kTimedOut);
+    UMA_HISTOGRAM_BOOLEAN("Tab.Organization.Proactive.Clicked", false);
+  }
 
   // Hide the button if not pressed. Use locked expansion mode to avoid
   // disrupting the user.
-  HideTabOrganization();
+  HideTabOrganization(button);
 }
 
-void TabSearchContainer::SetLockedExpansionMode(LockedExpansionMode mode) {
+void TabSearchContainer::SetLockedExpansionMode(LockedExpansionMode mode,
+                                                TabOrganizationButton* button) {
   if (mode == LockedExpansionMode::kNone) {
     if (locked_expansion_mode_ == LockedExpansionMode::kWillShow) {
-      ExecuteShowTabOrganization();
+      ExecuteShowTabOrganization(locked_expansion_button_);
     } else if (locked_expansion_mode_ == LockedExpansionMode::kWillHide) {
-      ExecuteHideTabOrganization();
+      ExecuteHideTabOrganization(locked_expansion_button_);
     }
+    locked_expansion_button_ = nullptr;
   } else {
+    locked_expansion_button_ = button;
     mouse_watcher_->Start(GetWidget()->GetNativeWindow());
   }
   locked_expansion_mode_ = mode;
 }
 
-void TabSearchContainer::ExecuteShowTabOrganization() {
-  // browser_ may be null in tests
-  if (browser_ &&
+void TabSearchContainer::ExecuteShowTabOrganization(
+    TabOrganizationButton* button) {
+  if (browser_ && (button == auto_tab_group_button_) &&
       !TabOrganizationUtils::GetInstance()->IsEnabled(browser_->profile())) {
     return;
   }
@@ -219,51 +402,47 @@
 
   scoped_tab_strip_modal_ui_ = tab_strip_model_->ShowModalUI();
 
-  expansion_animation_.SetSlideDuration(
-      GetAnimationDuration(kExpansionInDuration));
-
-  flat_edge_animation_.SetSlideDuration(
-      GetAnimationDuration(kFlatEdgeInDuration));
-  flat_edge_animation_.SetTweenType(gfx::Tween::Type::LINEAR);
-
-  opacity_animation_.SetSlideDuration(GetAnimationDuration(kOpacityInDuration));
-  const base::TimeDelta delay = GetAnimationDuration(kOpacityDelay);
-  opacity_animation_delay_timer_.Start(
-      FROM_HERE, delay, this, &TabSearchContainer::ShowOpacityAnimation);
-
-  expansion_animation_.Show();
-  flat_edge_animation_.Show();
+  animation_session_ = std::make_unique<TabOrganizationAnimationSession>(
+      button, this, TabOrganizationAnimationSession::AnimationSessionType::SHOW,
+      base::BindOnce(&TabSearchContainer::OnAnimationSessionEnded,
+                     base::Unretained(this)));
+  animation_session_->Start();
 
   hide_tab_organization_timer_.Start(
-      FROM_HERE, kShowDuration, this,
-      &TabSearchContainer::OnOrganizeButtonTimeout);
+      FROM_HERE, kShowDuration,
+      base::BindOnce(&TabSearchContainer::OnOrganizeButtonTimeout,
+                     base::Unretained(this), button));
 }
 
-void TabSearchContainer::ShowOpacityAnimation() {
-  opacity_animation_.Show();
-}
+void TabSearchContainer::ExecuteHideTabOrganization(
+    TabOrganizationButton* button) {
+  // Hide the current animation if the shown button is the same button. Do not
+  // create a new animation session.
+  if (animation_session_ &&
+      animation_session_->session_type() ==
+          TabOrganizationAnimationSession::AnimationSessionType::SHOW &&
+      animation_session_->button() == button) {
+    hide_tab_organization_timer_.Stop();
+    animation_session_->Hide();
+    return;
+  }
 
-void TabSearchContainer::ExecuteHideTabOrganization() {
+  if (!button->GetVisible()) {
+    return;
+  }
+
   // Stop the timer since the chip might be getting hidden on user actions like
   // dismissal or click and not timeout.
   hide_tab_organization_timer_.Stop();
-
-  expansion_animation_.SetSlideDuration(
-      GetAnimationDuration(kExpansionOutDuration));
-  expansion_animation_.Hide();
-
-  flat_edge_animation_.SetSlideDuration(
-      GetAnimationDuration(kFlatEdgeOutDuration));
-  flat_edge_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100);
-  flat_edge_animation_.Hide();
-
-  opacity_animation_.SetSlideDuration(
-      GetAnimationDuration(kOpacityOutDuration));
-  opacity_animation_.Hide();
+  animation_session_ = std::make_unique<TabOrganizationAnimationSession>(
+      button, this, TabOrganizationAnimationSession::AnimationSessionType::HIDE,
+      base::BindOnce(&TabSearchContainer::OnAnimationSessionEnded,
+                     base::Unretained(this)));
+  animation_session_->Start();
 }
 
 void TabSearchContainer::MouseMovedOutOfHost() {
-  SetLockedExpansionMode(LockedExpansionMode::kNone);
+  SetLockedExpansionMode(LockedExpansionMode::kNone, nullptr);
 }
 
 void TabSearchContainer::AnimationCanceled(const gfx::Animation* animation) {
@@ -271,51 +450,71 @@
 }
 
 void TabSearchContainer::AnimationEnded(const gfx::Animation* animation) {
-  ApplyAnimationValue(animation);
+  animation_session_->ApplyAnimationValue(animation);
+  animation_session_->MarkAnimationDone(animation);
+}
+
+void TabSearchContainer::OnAnimationSessionEnded() {
   // If the button went from shown -> hidden, unblock the tab strip from
-  // showing other modal UIs. Compare to 0.5 to distinguish between show/hide
-  // while avoiding potentially inexact float comparison to 0.0.
-  if (animation == &expansion_animation_ &&
-      animation->GetCurrentValue() < 0.5 && scoped_tab_strip_modal_ui_) {
+  // showing other modal UIs.
+  if (animation_session_->session_type() ==
+      TabOrganizationAnimationSession::AnimationSessionType::HIDE) {
     scoped_tab_strip_modal_ui_.reset();
   }
+
+  animation_session_.reset();
 }
 
 void TabSearchContainer::AnimationProgressed(const gfx::Animation* animation) {
-  ApplyAnimationValue(animation);
-}
-
-void TabSearchContainer::ApplyAnimationValue(const gfx::Animation* animation) {
-  float value = animation->GetCurrentValue();
-  if (animation == &expansion_animation_) {
-    tab_organization_button_->SetWidthFactor(value);
-  } else if (animation == &flat_edge_animation_) {
-    tab_search_button_->SetFlatEdgeFactor(1 - value);
-    tab_organization_button_->SetFlatEdgeFactor(1 - value);
-  } else if (animation == &opacity_animation_) {
-    tab_organization_button_->SetOpacity(value);
-  }
-}
-
-base::TimeDelta TabSearchContainer::GetAnimationDuration(
-    base::TimeDelta duration) {
-  return gfx::Animation::ShouldRenderRichAnimation() ? duration
-                                                     : base::TimeDelta();
+  animation_session_->ApplyAnimationValue(animation);
 }
 
 void TabSearchContainer::OnToggleActionUIState(const Browser* browser,
                                                bool should_show) {
   CHECK(tab_organization_service_);
+
+  if (locked_expansion_mode_ != LockedExpansionMode::kNone) {
+    return;
+  }
+
   if (should_show && browser_ == browser) {
-    ShowTabOrganization();
+    ShowTabOrganization(auto_tab_group_button_);
   } else {
-    HideTabOrganization();
+    HideTabOrganization(auto_tab_group_button_);
   }
 }
 
+void TabSearchContainer::OnTabDeclutterButtonClicked() {
+  const int tab_organization_tab_index = 1;
+  tab_search_button_->tab_search_bubble_host()->ShowTabSearchBubble(
+      false, tab_organization_tab_index);
+
+  tab_declutter_controller_->OnActionUIAccepted(
+      base::PassKey<TabSearchContainer>());
+
+  // Force hide the button when pressed, bypassing locked expansion mode.
+  ExecuteHideTabOrganization(tab_declutter_button_);
+}
+
+void TabSearchContainer::OnTabDeclutterButtonDismissed() {
+  tab_declutter_controller_->OnActionUIDismissed(
+      base::PassKey<TabSearchContainer>());
+
+  // Force hide the button when pressed, bypassing locked expansion mode.
+  ExecuteHideTabOrganization(tab_declutter_button_);
+}
+
 void TabSearchContainer::OnTriggerDeclutterUIVisibility(bool should_show) {
-  // TODO(b/358382459): Implement logic to show and hide the tab declutter
-  // nudge.
+  CHECK(tab_declutter_controller_);
+  if (locked_expansion_mode_ != LockedExpansionMode::kNone) {
+    return;
+  }
+
+  if (should_show) {
+    ShowTabOrganization(tab_declutter_button_);
+  } else {
+    HideTabOrganization(tab_declutter_button_);
+  }
 }
 
 BEGIN_METADATA(TabSearchContainer)
diff --git a/chrome/browser/ui/views/tabs/tab_search_container.h b/chrome/browser/ui/views/tabs/tab_search_container.h
index e526f5066..0025599 100644
--- a/chrome/browser/ui/views/tabs/tab_search_container.h
+++ b/chrome/browser/ui/views/tabs/tab_search_container.h
@@ -43,6 +43,50 @@
   METADATA_HEADER(TabSearchContainer, views::View)
 
  public:
+  class TabOrganizationAnimationSession {
+   public:
+    enum class AnimationSessionType { SHOW, HIDE };
+
+    TabOrganizationAnimationSession(
+        TabOrganizationButton* button,
+        TabSearchContainer* container,
+        AnimationSessionType session_type,
+        base::OnceCallback<void()> on_animation_ended);
+    ~TabOrganizationAnimationSession();
+    void ApplyAnimationValue(const gfx::Animation* animation);
+    void MarkAnimationDone(const gfx::Animation* animation);
+    void Start();
+    AnimationSessionType session_type() { return session_type_; }
+
+    gfx::SlideAnimation* expansion_animation() { return &expansion_animation_; }
+    void ResetAnimationForTesting(double value);
+
+    void Hide();
+    TabOrganizationButton* button() { return button_; }
+
+   private:
+    base::TimeDelta GetAnimationDuration(base::TimeDelta duration);
+    void ShowOpacityAnimation();
+    void Show();
+    raw_ptr<TabOrganizationButton> button_;
+    raw_ptr<TabSearchContainer> container_;
+
+    gfx::SlideAnimation expansion_animation_;
+    gfx::SlideAnimation flat_edge_animation_;
+    gfx::SlideAnimation opacity_animation_;
+
+    bool expansion_animation_done_ = false;
+    bool flat_edge_animation_done_ = false;
+    bool opacity_animation_done_ = false;
+    AnimationSessionType session_type_;
+
+    // Timer for initiating the opacity animation during show.
+    base::OneShotTimer opacity_animation_delay_timer_;
+
+    // Callback to container after animation has ended.
+    base::OnceCallback<void()> on_animation_ended_;
+  };
+
   TabSearchContainer(TabStripController* tab_strip_controller,
                      TabStripModel* tab_strip_model,
                      bool before_tab_strip,
@@ -52,26 +96,36 @@
   TabSearchContainer& operator=(const TabSearchContainer&) = delete;
   ~TabSearchContainer() override;
 
-  TabOrganizationButton* tab_organization_button() {
-    return tab_organization_button_;
+  TabOrganizationButton* auto_tab_group_button() {
+    return auto_tab_group_button_;
   }
+
+  TabOrganizationButton* tab_declutter_button() {
+    return tab_declutter_button_;
+  }
+
   TabSearchButton* tab_search_button() { return tab_search_button_; }
 
-  gfx::SlideAnimation* expansion_animation_for_testing() {
-    return &expansion_animation_;
+  TabOrganizationAnimationSession* animation_session_for_testing() {
+    return animation_session_.get();
   }
 
   TabOrganizationService* tab_organization_service_for_testing() {
     return tab_organization_service_;
   }
 
-  void ShowTabOrganization();
-  void HideTabOrganization();
-  void SetLockedExpansionModeForTesting(LockedExpansionMode mode);
+  void ShowTabOrganization(TabOrganizationButton* button);
+  void HideTabOrganization(TabOrganizationButton* button);
+  void SetLockedExpansionModeForTesting(LockedExpansionMode mode,
+                                        TabOrganizationButton* button);
 
-  void OnOrganizeButtonClicked();
-  void OnOrganizeButtonDismissed();
-  void OnOrganizeButtonTimeout();
+  void OnAutoTabGroupButtonClicked();
+  void OnAutoTabGroupButtonDismissed();
+
+  void OnTabDeclutterButtonClicked();
+  void OnTabDeclutterButtonDismissed();
+
+  void OnOrganizeButtonTimeout(TabOrganizationButton* button);
 
   // views::MouseWatcherListener:
   void MouseMovedOutOfHost() override;
@@ -88,19 +142,32 @@
   void OnTriggerDeclutterUIVisibility(bool should_show) override;
 
  private:
-  void SetLockedExpansionMode(LockedExpansionMode mode);
-  void ExecuteShowTabOrganization();
-  void ShowOpacityAnimation();
-  void ExecuteHideTabOrganization();
-  void ApplyAnimationValue(const gfx::Animation* animation);
-  base::TimeDelta GetAnimationDuration(base::TimeDelta duration);
+  void SetLockedExpansionMode(LockedExpansionMode mode,
+                              TabOrganizationButton* button);
+  void ExecuteShowTabOrganization(TabOrganizationButton* button);
+  void ExecuteHideTabOrganization(TabOrganizationButton* button);
+
+  void OnAnimationSessionEnded();
+
+  std::unique_ptr<TabOrganizationButton> CreateAutoTabGroupButton(
+      TabStripController* tab_strip_controller,
+      bool before_tab_strip);
+  std::unique_ptr<TabOrganizationButton> CreateTabDeclutterButton(
+      TabStripController* tab_strip_controller,
+      bool before_tab_strip);
+  void SetupButtonProperties(TabOrganizationButton* button,
+                             bool before_tab_strip);
 
   // View where, if the mouse is currently over its bounds, the expansion state
   // will not change. Changes will be staged until after the mouse exits the
   // bounds of this View.
   raw_ptr<View, DanglingUntriaged> locked_expansion_view_;
-  raw_ptr<TabOrganizationButton, DanglingUntriaged> tab_organization_button_ =
+
+  // The button currently holding the lock to be shown/hidden.
+  raw_ptr<TabOrganizationButton> locked_expansion_button_ = nullptr;
+  raw_ptr<TabOrganizationButton, DanglingUntriaged> auto_tab_group_button_ =
       nullptr;
+  raw_ptr<TabOrganizationButton> tab_declutter_button_ = nullptr;
   raw_ptr<TabSearchButton, DanglingUntriaged> tab_search_button_ = nullptr;
   raw_ptr<TabOrganizationService, DanglingUntriaged> tab_organization_service_ =
       nullptr;
@@ -109,15 +176,8 @@
   raw_ptr<const Browser> browser_;
   const raw_ptr<TabStripModel> tab_strip_model_;
 
-  // Animations controlling showing and hiding of tab_organization_button_.
-  gfx::SlideAnimation expansion_animation_{this};
-  gfx::SlideAnimation flat_edge_animation_{this};
-  gfx::SlideAnimation opacity_animation_{this};
-
   // Timer for hiding tab_organization_button_ after show.
   base::OneShotTimer hide_tab_organization_timer_;
-  // Timer for initiating the opacity animation during show.
-  base::OneShotTimer opacity_animation_delay_timer_;
 
   // When locked, the container is unable to change its expanded state. Changes
   // will be staged until after this is unlocked.
@@ -135,6 +195,8 @@
 
   // Prevents other features from showing tabstrip-modal UI.
   std::unique_ptr<ScopedTabStripModalUI> scoped_tab_strip_modal_ui_;
+
+  std::unique_ptr<TabOrganizationAnimationSession> animation_session_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_SEARCH_CONTAINER_H_
diff --git a/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc b/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc
index a7e71ab0..35ca5345 100644
--- a/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc
@@ -26,11 +26,13 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/events/test/event_generator.h"
+#include "ui/gfx/animation/slide_animation.h"
 
 class TabSearchContainerBrowserTest : public InProcessBrowserTest {
  public:
   TabSearchContainerBrowserTest() {
-    feature_list_.InitWithFeatures({features::kTabOrganization}, {});
+    feature_list_.InitWithFeatures(
+        {features::kTabOrganization, features::kTabstripDeclutter}, {});
     TabOrganizationUtils::GetInstance()->SetIgnoreOptGuideForTesting(true);
   }
 
@@ -58,19 +60,20 @@
 #endif
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
                        MAYBE_TogglesActionUIState) {
-  ASSERT_FALSE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
 
   TabOrganizationService* service =
       tab_search_container()->tab_organization_service_for_testing();
 
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone);
+      LockedExpansionMode::kNone, nullptr);
   tab_strip_model()->ForceShowingModalUIForTesting(false);
   service->OnTriggerOccured(browser());
 
-  ASSERT_TRUE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsShowing());
 }
 
 // TODO(crbug.com/338649929): Flaky on Windows 10 builds.
@@ -89,8 +92,7 @@
           ->tab_strip_region_view()
           ->tab_search_container();
 
-  ASSERT_FALSE(
-      second_search_container->expansion_animation_for_testing()->IsShowing());
+  ASSERT_FALSE(second_search_container->animation_session_for_testing());
 
   TabOrganizationService* service =
       tab_search_container()->tab_organization_service_for_testing();
@@ -99,17 +101,18 @@
             second_search_container->tab_organization_service_for_testing());
 
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone);
+      LockedExpansionMode::kNone, nullptr);
   tab_strip_model()->ForceShowingModalUIForTesting(false);
   second_search_container->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone);
+      LockedExpansionMode::kNone, nullptr);
   second_browser->tab_strip_model()->ForceShowingModalUIForTesting(false);
   service->OnTriggerOccured(browser());
 
-  EXPECT_TRUE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
-  EXPECT_FALSE(
-      second_search_container->expansion_animation_for_testing()->IsShowing());
+  EXPECT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsShowing());
+  EXPECT_FALSE(second_search_container->animation_session_for_testing());
 }
 
 // TODO(crbug.com/338649929): Flaky on Windows 10 builds.
@@ -122,136 +125,193 @@
 #endif
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
                        MAYBE_DoesntShowIfTabStripModalUIExists) {
-  ASSERT_FALSE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
 
   tab_strip_model()->ForceShowingModalUIForTesting(true);
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone);
-  tab_search_container()->ShowTabOrganization();
+      LockedExpansionMode::kNone, nullptr);
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
 
-  EXPECT_FALSE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
+  EXPECT_FALSE(tab_search_container()->animation_session_for_testing());
 
   tab_strip_model()->ForceShowingModalUIForTesting(false);
-  tab_search_container()->ShowTabOrganization();
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
 
-  EXPECT_TRUE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
+  EXPECT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsShowing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
                        BlocksTabStripModalUIWhileShown) {
   ASSERT_TRUE(browser()->tab_strip_model()->CanShowModalUI());
 
-  tab_search_container()->ShowTabOrganization();
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
 
   EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI());
 
-  tab_search_container()->expansion_animation_for_testing()->Reset(1);
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI());
 
-  tab_search_container()->HideTabOrganization();
+  tab_search_container()->HideTabOrganization(
+      tab_search_container()->auto_tab_group_button());
 
   EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI());
 
-  tab_search_container()->expansion_animation_for_testing()->Reset(0);
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(0);
+
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   EXPECT_TRUE(browser()->tab_strip_model()->CanShowModalUI());
 }
 
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, DelaysShow) {
-  ASSERT_FALSE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
 
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillShow);
-  tab_search_container()->ShowTabOrganization();
+      LockedExpansionMode::kWillShow,
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
 
-  ASSERT_FALSE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
 
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone);
+      LockedExpansionMode::kNone, nullptr);
 
-  ASSERT_TRUE(
-      tab_search_container()->expansion_animation_for_testing()->IsShowing());
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsShowing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, DelaysHide) {
-  tab_search_container()->expansion_animation_for_testing()->Reset(1);
-  ASSERT_FALSE(
-      tab_search_container()->expansion_animation_for_testing()->IsClosing());
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
+
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
+
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
 
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillHide);
-  tab_search_container()->HideTabOrganization();
+      LockedExpansionMode::kWillHide,
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()->HideTabOrganization(
+      tab_search_container()->auto_tab_group_button());
 
-  ASSERT_FALSE(
-      tab_search_container()->expansion_animation_for_testing()->IsClosing());
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
 
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone);
+      LockedExpansionMode::kNone, nullptr);
 
-  ASSERT_TRUE(
-      tab_search_container()->expansion_animation_for_testing()->IsClosing());
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsClosing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
                        ImmediatelyHidesWhenOrganizeButtonClicked) {
-  tab_search_container()->expansion_animation_for_testing()->Reset(1);
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
+
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillHide);
+      LockedExpansionMode::kWillHide,
+      tab_search_container()->auto_tab_group_button());
 
-  tab_search_container()->OnOrganizeButtonClicked();
+  tab_search_container()->OnAutoTabGroupButtonClicked();
 
-  EXPECT_TRUE(
-      tab_search_container()->expansion_animation_for_testing()->IsClosing());
+  EXPECT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsClosing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
                        ImmediatelyHidesWhenOrganizeButtonDismissed) {
-  tab_search_container()->expansion_animation_for_testing()->Reset(1);
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
+
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillHide);
+      LockedExpansionMode::kWillHide,
+      tab_search_container()->auto_tab_group_button());
 
-  tab_search_container()->OnOrganizeButtonDismissed();
+  tab_search_container()->OnAutoTabGroupButtonDismissed();
 
-  EXPECT_TRUE(
-      tab_search_container()->expansion_animation_for_testing()->IsClosing());
+  EXPECT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsClosing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
                        DelayedHidesWhenOrganizeButtonTimesOut) {
-  tab_search_container()->expansion_animation_for_testing()->Reset(1);
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillHide);
-
-  tab_search_container()->OnOrganizeButtonTimeout();
-
-  EXPECT_FALSE(
-      tab_search_container()->expansion_animation_for_testing()->IsClosing());
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone);
+      LockedExpansionMode::kWillHide,
+      tab_search_container()->auto_tab_group_button());
 
-  ASSERT_TRUE(
-      tab_search_container()->expansion_animation_for_testing()->IsClosing());
+  tab_search_container()->OnOrganizeButtonTimeout(
+      tab_search_container()->auto_tab_group_button());
+
+  EXPECT_FALSE(tab_search_container()->animation_session_for_testing());
+
+  tab_search_container()->SetLockedExpansionModeForTesting(
+      LockedExpansionMode::kNone,
+      tab_search_container()->auto_tab_group_button());
+
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsClosing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
                        LogsSuccessWhenButtonClicked) {
   base::HistogramTester histogram_tester;
 
-  tab_search_container()->expansion_animation_for_testing()->Reset(1);
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   TabOrganizationService* service =
       tab_search_container()->tab_organization_service_for_testing();
 
   service->OnTriggerOccured(browser());
 
-  tab_search_container()->OnOrganizeButtonClicked();
+  tab_search_container()->OnAutoTabGroupButtonClicked();
 
   histogram_tester.ExpectUniqueSample("Tab.Organization.AllEntrypoints.Clicked",
                                       true, 1);
@@ -264,14 +324,19 @@
                        LogsFailureWhenButtonDismissed) {
   base::HistogramTester histogram_tester;
 
-  tab_search_container()->expansion_animation_for_testing()->Reset(1);
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   TabOrganizationService* service =
       tab_search_container()->tab_organization_service_for_testing();
 
   service->OnTriggerOccured(browser());
 
-  tab_search_container()->OnOrganizeButtonDismissed();
+  tab_search_container()->OnAutoTabGroupButtonDismissed();
 
   histogram_tester.ExpectUniqueSample("Tab.Organization.Proactive.Clicked",
                                       false, 1);
@@ -282,17 +347,127 @@
                        LogsFailureWhenButtonTimeout) {
   base::HistogramTester histogram_tester;
 
-  tab_search_container()->expansion_animation_for_testing()->Reset(1);
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
 
   TabOrganizationService* service =
       tab_search_container()->tab_organization_service_for_testing();
 
   service->OnTriggerOccured(browser());
 
-  tab_search_container()->OnOrganizeButtonTimeout();
+  tab_search_container()->OnOrganizeButtonTimeout(
+      tab_search_container()->auto_tab_group_button());
 
   histogram_tester.ExpectUniqueSample("Tab.Organization.Proactive.Clicked",
                                       false, 1);
 
   histogram_tester.ExpectUniqueSample("Tab.Organization.Trigger.Outcome", 2, 1);
 }
+
+IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
+                       HidesAutoTabGroupButtonFromHalfway) {
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
+
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsShowing());
+
+  gfx::SlideAnimation* expansion_animation =
+      tab_search_container()
+          ->animation_session_for_testing()
+          ->expansion_animation();
+
+  gfx::AnimationTestApi animation_api(expansion_animation);
+  base::TimeTicks now = base::TimeTicks::Now();
+  animation_api.SetStartTime(now);
+  animation_api.Step(now + (expansion_animation->GetSlideDuration() / 2));
+
+  double expanded_value = expansion_animation->GetCurrentValue();
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
+
+  tab_search_container()->HideTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+
+  EXPECT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsClosing());
+
+  EXPECT_EQ(tab_search_container()
+                ->animation_session_for_testing()
+                ->expansion_animation()
+                ->GetCurrentValue(),
+            expanded_value);
+}
+
+IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, ShowsDeclutterChip) {
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
+
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->tab_declutter_button());
+
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsShowing());
+}
+
+IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
+                       ShowsAndHidesDeclutterChip) {
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
+
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->tab_declutter_button());
+
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsShowing());
+
+  // Finish showing declutter chip.
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
+
+  // Hide the declutter chip.
+  tab_search_container()->HideTabOrganization(
+      tab_search_container()->tab_declutter_button());
+
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsClosing());
+}
+
+IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
+                       DoesNotShowDeclutterChipWhenAutoTabGroupChipIsShown) {
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
+
+  // Show the auto-tab group chip.
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->auto_tab_group_button());
+
+  ASSERT_TRUE(tab_search_container()
+                  ->animation_session_for_testing()
+                  ->expansion_animation()
+                  ->IsShowing());
+  tab_search_container()
+      ->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
+  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
+
+  // Try to show the declutter chip while auto-tab group chip is already shown.
+  tab_search_container()->ShowTabOrganization(
+      tab_search_container()->tab_declutter_button());
+
+  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
+}
diff --git a/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc b/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc
index eec5efa..cacc1ca 100644
--- a/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc
@@ -72,43 +72,49 @@
 TEST_F(TabSearchContainerTest, OrdersButtonsCorrectly) {
   ASSERT_EQ(container_before_tab_strip_->tab_search_button(),
             container_before_tab_strip_->children()[0]);
-  ASSERT_EQ(container_before_tab_strip_->tab_organization_button(),
+  ASSERT_EQ(container_before_tab_strip_->tab_declutter_button(),
             container_before_tab_strip_->children()[1]);
+  ASSERT_EQ(container_before_tab_strip_->auto_tab_group_button(),
+            container_before_tab_strip_->children()[2]);
 
-  ASSERT_EQ(container_after_tab_strip_->tab_organization_button(),
+  ASSERT_EQ(container_after_tab_strip_->tab_declutter_button(),
             container_after_tab_strip_->children()[0]);
-  ASSERT_EQ(container_after_tab_strip_->tab_search_button(),
+  ASSERT_EQ(container_after_tab_strip_->auto_tab_group_button(),
             container_after_tab_strip_->children()[1]);
+  ASSERT_EQ(container_after_tab_strip_->tab_search_button(),
+            container_after_tab_strip_->children()[2]);
 }
 
 TEST_F(TabSearchContainerTest, ButtonsHaveFlatEdges) {
   ASSERT_EQ(Edge::kRight,
             container_before_tab_strip_->tab_search_button()->flat_edge());
-  ASSERT_EQ(
-      Edge::kLeft,
-      container_before_tab_strip_->tab_organization_button()->flat_edge());
+  ASSERT_EQ(Edge::kLeft,
+            container_before_tab_strip_->auto_tab_group_button()->flat_edge());
 
   ASSERT_EQ(Edge::kLeft,
             container_after_tab_strip_->tab_search_button()->flat_edge());
   ASSERT_EQ(Edge::kRight,
-            container_after_tab_strip_->tab_organization_button()->flat_edge());
+            container_after_tab_strip_->auto_tab_group_button()->flat_edge());
 }
 
 TEST_F(TabSearchContainerTest, AnimatesToExpanded) {
   // Should be collapsed by default
-  ASSERT_EQ(0, container_before_tab_strip_->expansion_animation_for_testing()
-                   ->GetCurrentValue());
+  ASSERT_EQ(nullptr,
+            container_before_tab_strip_->animation_session_for_testing());
 
-  ASSERT_EQ(0, container_before_tab_strip_->tab_organization_button()
+  ASSERT_EQ(0, container_before_tab_strip_->auto_tab_group_button()
                    ->width_factor_for_testing());
 
-  container_before_tab_strip_->ShowTabOrganization();
+  container_before_tab_strip_->ShowTabOrganization(
+      container_before_tab_strip_->auto_tab_group_button());
 
-  ASSERT_TRUE(container_before_tab_strip_->expansion_animation_for_testing()
+  ASSERT_TRUE(container_before_tab_strip_->animation_session_for_testing()
+                  ->expansion_animation()
                   ->IsShowing());
 
-  container_before_tab_strip_->expansion_animation_for_testing()->Reset(1);
+  container_before_tab_strip_->animation_session_for_testing()
+      ->ResetAnimationForTesting(1);
 
-  ASSERT_EQ(1, container_before_tab_strip_->tab_organization_button()
+  ASSERT_EQ(1, container_before_tab_strip_->auto_tab_group_button()
                    ->width_factor_for_testing());
 }
diff --git a/chrome/browser/ui/views/toolbar/BUILD.gn b/chrome/browser/ui/views/toolbar/BUILD.gn
index e368f860..e7a443d8 100644
--- a/chrome/browser/ui/views/toolbar/BUILD.gn
+++ b/chrome/browser/ui/views/toolbar/BUILD.gn
@@ -116,7 +116,6 @@
     "//chrome/browser/ui/actions:actions_headers",
     "//chrome/browser/ui/browser_window",
     "//chrome/browser/ui/color:mixers",
-    "//chrome/browser/ui/customize_chrome",
     "//chrome/browser/ui/tabs:tab_enums",
     "//chrome/browser/ui/tabs:tab_strip_model_observer",
     "//chrome/browser/ui/views/side_panel:side_panel_enums",
diff --git a/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.cc b/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.cc
index 65803837..c78bb82 100644
--- a/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.cc
@@ -14,8 +14,6 @@
 #include "chrome/browser/ui/browser_actions.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
-#include "chrome/browser/ui/customize_chrome/side_panel_controller.h"
-#include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_action_callback.h"
 #include "chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container.h"
@@ -347,13 +345,6 @@
   if (command_id == IDC_UPDATE_SIDE_PANEL_PIN_STATE) {
     return browser_->profile()->IsRegularProfile() && is_pinnable_;
   }
-  if (command_id == IDC_SHOW_CUSTOMIZE_CHROME_TOOLBAR) {
-    tabs::TabModel* tab = browser_->tab_strip_model()->GetActiveTab();
-    customize_chrome::SidePanelController* side_panel_controller =
-        tab->tab_features()->customize_chrome_side_panel_controller();
-    return side_panel_controller &&
-           side_panel_controller->IsCustomizeChromeEntryAvailable();
-  }
   return true;
 }
 
diff --git a/chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container_browsertest.cc b/chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container_browsertest.cc
index ba485079..2fffc10 100644
--- a/chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container_browsertest.cc
@@ -109,18 +109,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PinnedToolbarActionsContainerBrowserTest,
-                       CustomizeToolbarCanNotBeCalledFromIncognitoWindow) {
-  Browser* incognito_browser = Browser::Create(Browser::CreateParams(
-      browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true),
-      true));
-  AddBlankTabAndShow(incognito_browser);
-  auto pinned_button = std::make_unique<PinnedActionToolbarButton>(
-      incognito_browser, actions::kActionCut, container());
-  EXPECT_FALSE(
-      pinned_button->IsCommandIdEnabled(IDC_SHOW_CUSTOMIZE_CHROME_TOOLBAR));
-}
-
-IN_PROC_BROWSER_TEST_F(PinnedToolbarActionsContainerBrowserTest,
                        TranslateStatusIndicator) {
   PinnedToolbarActionsModel* const actions_model =
       PinnedToolbarActionsModel::Get(browser()->profile());
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
index 9ce6ef7..f6c5832 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
@@ -1002,6 +1002,7 @@
       header_icon_view_->SetVisible(false);
     } else {
       ConfigureBrandImageView(header_icon_view_, idp_metadata.brand_icon_url);
+      header_icon_view_->SetVisible(true);
     }
   }
   if (title.compare(title_) != 0) {
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
index 638c4c8b..98fd93d 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
@@ -55,14 +55,14 @@
 class AccountSelectionBubbleViewTest : public ChromeViewsTestBase,
                                        public AccountSelectionViewTestBase {
  public:
-  AccountSelectionBubbleViewTest()
-      : idp_data_(base::MakeRefCounted<content::IdentityProviderData>(
-            kIdpForDisplay,
-            content::IdentityProviderMetadata(),
-            CreateTestClientMetadata(kTermsOfServiceUrl),
-            blink::mojom::RpContext::kSignIn,
-            kDefaultDisclosureFields,
-            /*has_login_status_mismatch=*/false)) {}
+  AccountSelectionBubbleViewTest() {
+    content::IdentityProviderMetadata idp_metadata;
+    idp_metadata.brand_icon_url = GURL(kIdpBrandIconUrl);
+    idp_data_ = base::MakeRefCounted<content::IdentityProviderData>(
+        kIdpForDisplay, idp_metadata, CreateTestClientMetadata(),
+        blink::mojom::RpContext::kSignIn, kDefaultDisclosureFields,
+        /*has_login_status_mismatch=*/false);
+  }
 
  protected:
   void CreateAccountSelectionBubble(bool exclude_title) {
@@ -143,6 +143,13 @@
         static_cast<views::Label*>(GetViewWithClassName(header, "Label"));
     ASSERT_TRUE(title_view);
     EXPECT_EQ(title_view->GetText(), expected_title);
+
+    if (expect_idp_brand_icon_in_header) {
+      views::ImageView* idp_brand_icon = static_cast<views::ImageView*>(
+          GetViewWithClassName(header, "BrandIconImageView"));
+      ASSERT_TRUE(idp_brand_icon);
+      EXPECT_TRUE(idp_brand_icon->GetVisible());
+    }
   }
 
   void PerformMultiAccountChecks(views::View* container,
@@ -275,8 +282,7 @@
                          bool expect_idp_brand_icon_in_header) {
     CreateAccountSelectionBubble(
         /*exclude_title=*/false);
-    dialog_->ShowFailureDialog(
-        kIdpETLDPlusOne, content::IdentityProviderMetadata());
+    dialog_->ShowFailureDialog(kIdpETLDPlusOne, idp_data_->idp_metadata);
 
     const std::vector<raw_ptr<views::View, VectorExperimental>> children =
         dialog()->children();
@@ -315,7 +321,7 @@
     CreateAccountSelectionBubble(
         /*exclude_title=*/false);
     dialog_->ShowErrorDialog(
-        kIdpETLDPlusOne, content::IdentityProviderMetadata(),
+        kIdpETLDPlusOne, idp_data_->idp_metadata,
         content::IdentityCredentialTokenError(error_code, error_url));
 
     const std::vector<raw_ptr<views::View, VectorExperimental>> children =
diff --git a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
index 2015858f..c997586 100644
--- a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
+++ b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/ash/crosapi/url_handler_ash.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/system_web_apps/apps/os_url_handler_system_web_app_info.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
@@ -41,6 +40,7 @@
 #include "chrome/browser/profiles/delete_profile_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
diff --git a/chrome/browser/ui/webui/about/about_ui_unittest.cc b/chrome/browser/ui/webui/about/about_ui_unittest.cc
index ea1c214..67b71de 100644
--- a/chrome/browser/ui/webui/about/about_ui_unittest.cc
+++ b/chrome/browser/ui/webui/about/about_ui_unittest.cc
@@ -32,8 +32,8 @@
 #include "ash/webui/file_manager/url_constants.h"
 #include "base/files/scoped_temp_dir.h"
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
-#include "chrome/browser/ash/login/ui/fake_login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/fake_login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/demo_preferences_screen_handler.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
diff --git a/chrome/browser/ui/webui/ash/OWNERS b/chrome/browser/ui/webui/ash/OWNERS
index 33b0c69..9319ad5 100644
--- a/chrome/browser/ui/webui/ash/OWNERS
+++ b/chrome/browser/ui/webui/ash/OWNERS
@@ -2,6 +2,5 @@
 
 per-file *bluetooth*=file://ash/webui/common/resources/bluetooth/OWNERS
 per-file *net*=file://ash/webui/common/resources/network/OWNERS
-per-file drive_internals_ui.*=file://ui/file_manager/OWNERS
 per-file cros_components*=file://third_party/cros-components/OWNERS
 per-file set_time_ui*=file://chrome/browser/resources/ash/settings/OWNERS
diff --git a/chrome/browser/ui/webui/ash/assistant_optin/BUILD.gn b/chrome/browser/ui/webui/ash/assistant_optin/BUILD.gn
index 7a6207d..b4aaaa3 100644
--- a/chrome/browser/ui/webui/ash/assistant_optin/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/assistant_optin/BUILD.gn
@@ -37,9 +37,9 @@
     "//chrome/app:generated_resources",
     "//chrome/browser:resources",
     "//chrome/browser/ash/assistant",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/resources/chromeos/assistant_optin:resources",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui:webui_util",
     "//chromeos/ash/components/assistant:buildflags",
     "//chromeos/ash/components/audio",
diff --git a/chrome/browser/ui/webui/ash/assistant_optin/DEPS b/chrome/browser/ui/webui/ash/assistant_optin/DEPS
index 86382750c..d446da2 100644
--- a/chrome/browser/ui/webui/ash/assistant_optin/DEPS
+++ b/chrome/browser/ui/webui/ash/assistant_optin/DEPS
@@ -11,10 +11,10 @@
   # refactor //chrome/browser/ui/ash to break these dependencies; see b/332804822.
   # Whenever possible, avoid adding new //chrome dependencies to this list.
   "+chrome/browser/ash/assistant",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/consent_auditor",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/views/chrome_web_dialog_view.h",
   "+chrome/browser/ui/webui/ash/login",
   "+chrome/browser/ui/webui/ash/system_web_dialog",
diff --git a/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc
index 54f0d955..6458891 100644
--- a/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc
+++ b/chrome/browser/ui/webui/ash/assistant_optin/assistant_optin_ui.cc
@@ -21,9 +21,9 @@
 #include "base/values.h"
 #include "build/buildflag.h"
 #include "chrome/browser/ash/assistant/assistant_util.h"
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 #include "chrome/browser/ui/views/chrome_web_dialog_view.h"
 #include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
diff --git a/chrome/browser/ui/webui/ash/drive_internals/DIR_METADATA b/chrome/browser/ui/webui/ash/drive_internals/DIR_METADATA
new file mode 100644
index 0000000..e9400b87
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/drive_internals/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//ui/file_manager/COMMON_METADATA"
diff --git a/chrome/browser/ui/webui/ash/drive_internals/OWNERS b/chrome/browser/ui/webui/ash/drive_internals/OWNERS
new file mode 100644
index 0000000..73220a8
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/drive_internals/OWNERS
@@ -0,0 +1 @@
+file://ui/file_manager/OWNERS
diff --git a/chrome/browser/ui/webui/ash/extended_updates/BUILD.gn b/chrome/browser/ui/webui/ash/extended_updates/BUILD.gn
index f8ac745f..1d60b76 100644
--- a/chrome/browser/ui/webui/ash/extended_updates/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/extended_updates/BUILD.gn
@@ -39,9 +39,9 @@
     "//chrome/app:generated_resources",
     "//chrome/browser/apps/app_service",
     "//chrome/browser/ash/extended_updates",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/resources/ash/extended_updates:resources",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui:webui_util",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/common",
diff --git a/chrome/browser/ui/webui/ash/extended_updates/DEPS b/chrome/browser/ui/webui/ash/extended_updates/DEPS
index feab736c..9ccec9e 100644
--- a/chrome/browser/ui/webui/ash/extended_updates/DEPS
+++ b/chrome/browser/ui/webui/ash/extended_updates/DEPS
@@ -12,8 +12,8 @@
   # Whenever possible, avoid adding new //chrome dependencies to this list.
   "+chrome/browser/apps/app_service",
   "+chrome/browser/ash/extended_updates",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/webui/ash/login",
   "+chrome/browser/ui/webui/ash/system_web_dialog",
   "+chrome/browser/ui/webui/webui_util.h",
diff --git a/chrome/browser/ui/webui/ash/extended_updates/extended_updates_dialog.cc b/chrome/browser/ui/webui/ash/extended_updates/extended_updates_dialog.cc
index 4cbf14b..422ae23 100644
--- a/chrome/browser/ui/webui/ash/extended_updates/extended_updates_dialog.cc
+++ b/chrome/browser/ui/webui/ash/extended_updates/extended_updates_dialog.cc
@@ -9,8 +9,8 @@
 #include "ash/system/extended_updates/extended_updates_metrics.h"
 #include "base/logging.h"
 #include "chrome/browser/ash/extended_updates/extended_updates_controller.h"
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 #include "chrome/browser/ui/webui/ash/system_web_dialog/system_web_dialog_delegate.h"
 #include "chrome/common/webui_url_constants.h"
 #include "ui/aura/window.h"
diff --git a/chrome/browser/ui/webui/ash/lock_screen_reauth/BUILD.gn b/chrome/browser/ui/webui/ash/lock_screen_reauth/BUILD.gn
index b5cc741..785b815 100644
--- a/chrome/browser/ui/webui/ash/lock_screen_reauth/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/lock_screen_reauth/BUILD.gn
@@ -58,7 +58,6 @@
     "//chrome/browser:browser_process",
     "//chrome/browser:resources",
     "//chrome/browser/ash/login/lock/online_reauth",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/resources/chromeos/gaia_action_buttons:resources",
diff --git a/chrome/browser/ui/webui/ash/lock_screen_reauth/DEPS b/chrome/browser/ui/webui/ash/lock_screen_reauth/DEPS
index 969b6955..249fa38 100644
--- a/chrome/browser/ui/webui/ash/lock_screen_reauth/DEPS
+++ b/chrome/browser/ui/webui/ash/lock_screen_reauth/DEPS
@@ -12,7 +12,6 @@
   # Whenever possible, avoid adding new //chrome dependencies to this list.
   "+chrome/browser/ash/login",
   "+chrome/browser/ash/login/lock/online_reauth",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/policy/core",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/browser_process.h",
@@ -22,6 +21,7 @@
   "+chrome/browser/media/webrtc",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h",
   "+chrome/browser/ui/webui/ash/internet",
   "+chrome/browser/ui/webui/ash/login",
diff --git a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_dialogs.cc b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_dialogs.cc
index bd5237e..54610354 100644
--- a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_dialogs.cc
+++ b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_dialogs.cc
@@ -16,12 +16,12 @@
 #include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/ash/login/helper.h"
 #include "chrome/browser/ash/login/profile_auth_data.h"
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 #include "chrome/browser/ui/webui/ash/lock_screen_reauth/base_lock_dialog.h"
 #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_captive_portal_dialog.h"
 #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_network_dialog.h"
diff --git a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
index b2f2e47e..4fd4a3f 100644
--- a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
+++ b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
@@ -16,13 +16,13 @@
 #include "chrome/browser/ash/login/lock/online_reauth/lock_screen_reauth_manager_factory.h"
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/signin_partition_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/enterprise/util/managed_browser_utils.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_dialogs.h"
 #include "chrome/browser/ui/webui/ash/login/check_passwords_against_cryptohome_helper.h"
 #include "chrome/browser/ui/webui/ash/login/online_login_utils.h"
diff --git a/chrome/browser/ui/webui/ash/login/BUILD.gn b/chrome/browser/ui/webui/ash/login/BUILD.gn
index b027c61..3cffbeb 100644
--- a/chrome/browser/ui/webui/ash/login/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/login/BUILD.gn
@@ -199,6 +199,7 @@
     "//chrome/browser/ash/base",
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/policy/enrollment",
+    "//chrome/browser/ash/tpm",
     "//chrome/browser/ui/webui/ash/login/mojom",
     "//chrome/common",
     "//chromeos/ash/components/dbus/os_install",
@@ -263,7 +264,6 @@
     "//chrome/browser/ash/login/screens/osauth",
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/login/signin",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/login/users",
     "//chrome/browser/ash/multidevice_setup",
     "//chrome/browser/ash/policy/core",
@@ -347,7 +347,6 @@
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/screens/osauth",
     "//chrome/browser/ash/login/session",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/handlers",
     "//chrome/browser/ash/system",
     "//chrome/browser/ui/ash/login",
diff --git a/chrome/browser/ui/webui/ash/login/DEPS b/chrome/browser/ui/webui/ash/login/DEPS
index f8a46a7b..7b7feeea 100644
--- a/chrome/browser/ui/webui/ash/login/DEPS
+++ b/chrome/browser/ui/webui/ash/login/DEPS
@@ -28,7 +28,6 @@
   "+chrome/browser/ash/login/session",
   "+chrome/browser/ash/login/signin",
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/multidevice_setup",
   "+chrome/browser/ash/policy/core",
@@ -36,7 +35,7 @@
   "+chrome/browser/ash/profiles",
   "+chrome/browser/ash/settings",
   "+chrome/browser/ash/system",
-  "+chrome/browser/ash/tpm_firmware_update.h",
+  "+chrome/browser/ash/tpm",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/browser_process_platform_part.h",
   "+chrome/browser/certificate_provider",
@@ -47,6 +46,7 @@
   "+chrome/browser/profiles",
   "+chrome/browser/profiles",
   "+chrome/browser/signin",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/webui/about",
   "+chrome/browser/ui/webui/ash/assistant_optin",
   "+chrome/browser/ui/webui/ash/cellular_setup",
diff --git a/chrome/browser/ui/webui/ash/login/base_screen_handler.cc b/chrome/browser/ui/webui/ash/login/base_screen_handler.cc
index 1463f77..c0124da 100644
--- a/chrome/browser/ui/webui/ash/login/base_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/base_screen_handler.cc
@@ -9,8 +9,8 @@
 #include "base/strings/strcat.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
 #include "chrome/browser/ash/login/screens/base_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/base_webui_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 
diff --git a/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc b/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
index 0ae65064..eae3df5 100644
--- a/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/core_oobe_handler.cc
@@ -13,8 +13,8 @@
 #include "chrome/browser/ash/login/configuration_keys.h"
 #include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/grit/branded_strings.h"
diff --git a/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
index d945db8..6d8b069 100644
--- a/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/ash/login/help_app_launcher.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
 #include "chrome/browser/ash/login/signin_partition_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/core/policy_oauth2_token_fetcher.h"
@@ -32,6 +31,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/login/localized_values_builder.h"
diff --git a/chrome/browser/ui/webui/ash/login/error_screen_handler.cc b/chrome/browser/ui/webui/ash/login/error_screen_handler.cc
index b7c9e711..f03e3471 100644
--- a/chrome/browser/ui/webui/ash/login/error_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/error_screen_handler.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 
 #include "base/values.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/login/localized_values_builder.h"
 #include "ui/chromeos/devicetype_utils.h"
diff --git a/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
index ec6cad7..8dffb21 100644
--- a/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
@@ -48,10 +48,6 @@
 #include "chrome/browser/ash/login/session/user_session_manager.h"
 #include "chrome/browser/ash/login/signin_partition_manager.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
-#include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager_util.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -68,6 +64,10 @@
 #include "chrome/browser/policy/networking/device_network_configuration_updater_ash.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/chrome_device_id_helper.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
+#include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/webui/ash/login/cookie_waiter.h"
 #include "chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
diff --git a/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc b/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
index 8425e5c..d7f3658 100644
--- a/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/locale_switch_screen_handler.cc
@@ -9,7 +9,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/screens/locale_switch_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 
diff --git a/chrome/browser/ui/webui/ash/login/network_dropdown_handler.cc b/chrome/browser/ui/webui/ash/login/network_dropdown_handler.cc
index eb405a44..0ac7a44 100644
--- a/chrome/browser/ui/webui/ash/login/network_dropdown_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/network_dropdown_handler.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ui/webui/ash/login/network_dropdown_handler.h"
 
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/internet/internet_config_dialog.h"
 #include "chrome/browser/ui/webui/ash/internet/internet_detail_dialog.h"
 #include "chromeos/ash/components/network/network_handler.h"
diff --git a/chrome/browser/ui/webui/ash/login/network_state_informer.h b/chrome/browser/ui/webui/ash/login/network_state_informer.h
index a3deb63e..8b37d7d6 100644
--- a/chrome/browser/ui/webui/ash/login/network_state_informer.h
+++ b/chrome/browser/ui/webui/ash/login/network_state_informer.h
@@ -17,7 +17,7 @@
 #include "base/scoped_observation.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/screens/network_error.h"
-#include "chrome/browser/ash/login/ui/captive_portal_window_proxy.h"
+#include "chrome/browser/ui/ash/login/captive_portal_window_proxy.h"
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "chromeos/ash/components/network/network_state_handler_observer.h"
diff --git a/chrome/browser/ui/webui/ash/login/online_login_utils.cc b/chrome/browser/ui/webui/ash/login/online_login_utils.cc
index f15740c5..f6316f4 100644
--- a/chrome/browser/ui/webui/ash/login/online_login_utils.cc
+++ b/chrome/browser/ui/webui/ash/login/online_login_utils.cc
@@ -9,10 +9,10 @@
 #include "ash/constants/ash_features.h"
 #include "base/types/expected.h"
 #include "chrome/browser/ash/login/signin_partition_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/ui/ash/login/login_display_host_webui.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/installer/util/google_update_settings.h"
diff --git a/chrome/browser/ui/webui/ash/login/online_login_utils.h b/chrome/browser/ui/webui/ash/login/online_login_utils.h
index 0449809..c07ac4f7 100644
--- a/chrome/browser/ui/webui/ash/login/online_login_utils.h
+++ b/chrome/browser/ui/webui/ash/login/online_login_utils.h
@@ -12,8 +12,8 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ash/login/login_client_cert_usage_observer.h"
 #include "chrome/browser/ash/login/signin_partition_manager.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
-#include "chrome/browser/ash/login/ui/signin_ui.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/signin_ui.h"
 #include "chromeos/ash/components/login/auth/public/challenge_response_key.h"
 #include "chromeos/ash/components/login/auth/public/saml_password_attributes.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
index 39cc281..b9bab883 100644
--- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -38,7 +38,6 @@
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/ash/login/screens/error_screen.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
@@ -48,6 +47,7 @@
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/about/about_ui.h"
 #include "chrome/browser/ui/webui/ash/login/add_child_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/ai_intro_screen_handler.h"
diff --git a/chrome/browser/ui/webui/ash/login/reset_screen_handler.h b/chrome/browser/ui/webui/ash/login/reset_screen_handler.h
index 39e871d..8044d02 100644
--- a/chrome/browser/ui/webui/ash/login/reset_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/reset_screen_handler.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_RESET_SCREEN_HANDLER_H_
 
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
 
 namespace ash {
diff --git a/chrome/browser/ui/webui/ash/login/testapi/BUILD.gn b/chrome/browser/ui/webui/ash/login/testapi/BUILD.gn
index b7c9093a..3d2fee5f 100644
--- a/chrome/browser/ui/webui/ash/login/testapi/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/login/testapi/BUILD.gn
@@ -27,7 +27,6 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/quick_unlock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/core",
     "//chrome/browser/ash/policy/enrollment",
     "//chrome/browser/ui/ash/login",
@@ -59,7 +58,6 @@
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/test:test_support",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/policy/enrollment",
     "//chrome/browser/ui/webui/ash/login",
     "//chromeos/ash/components/hid_detection:test_support",
diff --git a/chrome/browser/ui/webui/ash/login/testapi/DEPS b/chrome/browser/ui/webui/ash/login/testapi/DEPS
index c2096263..22099ccbb 100644
--- a/chrome/browser/ui/webui/ash/login/testapi/DEPS
+++ b/chrome/browser/ui/webui/ash/login/testapi/DEPS
@@ -14,12 +14,10 @@
   "+chrome/browser/ash/login/quick_unlock",
   "+chrome/browser/ash/login/screens",
   "+chrome/browser/ash/login/test",
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/policy/core",
   "+chrome/browser/ash/policy/enrollment",
   "+chrome/browser/browser_process.h",
   "+chrome/browser/browser_process_platform_part.h",
   "+chrome/browser/ui/ash/login",
-  "+chrome/browser/ash/login",
   "+chrome/browser/ui/webui/ash/login",
 ]
diff --git a/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_browsertest.cc b/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_browsertest.cc
index c4dda9b1..b5f7f35 100644
--- a/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_browsertest.cc
@@ -14,9 +14,9 @@
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/test_condition_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/marketing_opt_in_screen_handler.h"
diff --git a/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc b/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
index a2fbb5f..354f78bc 100644
--- a/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/testapi/oobe_test_api_handler.cc
@@ -26,12 +26,12 @@
 #include "chrome/browser/ash/login/screens/network_screen.h"
 #include "chrome/browser/ash/login/screens/split_modifier_keyboard_info_screen.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
 #include "chrome/browser/ui/webui/ash/login/hid_detection_screen_handler.h"
 #include "chromeos/ash/components/assistant/buildflags.h"
diff --git a/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc b/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
index 01e3eb9..1c15dba6 100644
--- a/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/welcome_screen_handler.cc
@@ -20,13 +20,13 @@
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ash/login/screens/welcome_screen.h"
-#include "chrome/browser/ash/login/ui/input_events_blocker.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
 #include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/login/input_events_blocker.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h"
 #include "chrome/browser/ui/webui/ash/login/l10n_util.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
diff --git a/chrome/browser/ui/webui/ash/multidevice_setup/BUILD.gn b/chrome/browser/ui/webui/ash/multidevice_setup/BUILD.gn
index c3b7ebc2..40087f5 100644
--- a/chrome/browser/ui/webui/ash/multidevice_setup/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/multidevice_setup/BUILD.gn
@@ -37,12 +37,12 @@
     "//chrome/app:generated_resources",
     "//chrome/browser:resources",
     "//chrome/browser/ash/login/smart_lock",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/multidevice_setup",
     "//chrome/browser/ash/phonehub",
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/resources/chromeos:multidevice_setup_resources",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui:webui_util",
     "//chrome/browser/ui/webui/ash/login",
     "//chrome/browser/ui/webui/ash/user_image",
diff --git a/chrome/browser/ui/webui/ash/multidevice_setup/DEPS b/chrome/browser/ui/webui/ash/multidevice_setup/DEPS
index d685a86dc..8c55af6 100644
--- a/chrome/browser/ui/webui/ash/multidevice_setup/DEPS
+++ b/chrome/browser/ui/webui/ash/multidevice_setup/DEPS
@@ -10,10 +10,10 @@
   # Existing dependencies within //chrome. There is an active effort to
   # refactor //chrome/browser/ui/ash to break these dependencies; see b/332804822.
   # Whenever possible, avoid adding new //chrome dependencies to this list.
-  "+chrome/browser/ash/login/ui",
   "+chrome/browser/ash/multidevice_setup",
   "+chrome/browser/ash/profiles",
   "+chrome/browser/profiles",
+  "+chrome/browser/ui/ash/login",
   "+chrome/browser/ui/browser_dialogs.h",
   "+chrome/browser/ui/settings_window_manager_chromeos.h",
   "+chrome/browser/ui/webui/ash/system_web_dialog",
diff --git a/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc b/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc
index 637ab62..87440a6f 100644
--- a/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc
+++ b/chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.cc
@@ -17,10 +17,10 @@
 #include "base/functional/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
-#include "chrome/browser/ash/login/ui/oobe_dialog_size_utils.h"
 #include "chrome/browser/ash/multidevice_setup/multidevice_setup_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/oobe_dialog_size_utils.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_handler.h"
 #include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_localized_strings_provider.h"
diff --git a/chrome/browser/ui/webui/ash/os_feedback_dialog/BUILD.gn b/chrome/browser/ui/webui/ash/os_feedback_dialog/BUILD.gn
index 525e40ffc..95bf8f3 100644
--- a/chrome/browser/ui/webui/ash/os_feedback_dialog/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/os_feedback_dialog/BUILD.gn
@@ -17,15 +17,15 @@
   deps = [
     "//ash/webui/os_feedback_ui:url_constants",
     "//base",
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/os_feedback",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/ash/login",
     "//chrome/browser/ui/webui/ash/system_web_dialog",
     "//extensions/browser",
   ]
 
   allow_circular_includes_from = [
-    "//chrome/browser/ash/login/ui",
     "//chrome/browser/ash/os_feedback",
+    "//chrome/browser/ui/ash/login",
   ]
 }
diff --git a/chrome/browser/ui/webui/ash/settings/pages/a11y/accessibility_handler.cc b/chrome/browser/ui/webui/ash/settings/pages/a11y/accessibility_handler.cc
index 947fe955..41fa78f 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/a11y/accessibility_handler.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/a11y/accessibility_handler.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/dictation.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
-#include "chrome/browser/ash/url_handler/os_url_handler.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -186,22 +185,8 @@
     return;
   }
 
-  // If Lacros is the only browser, we need to open the options page in an Ash
-  // app window instead of a regular Ash browser window so that the user can't
-  // navigate in Ash. We do so using the OsUrlHandler SWA. Exception: Kiosk mode
-  // doesn't support SWA but already hide the navigation bar.
-  bool open_with_os_url_handler =
-      !crosapi::browser_util::IsAshWebBrowserEnabled() &&
-      !chromeos::IsKioskSession();
-  if (open_with_os_url_handler) {
-    DCHECK(extensions::OptionsPageInfo::ShouldOpenInTab(extension));
-    GURL url = extensions::OptionsPageInfo::GetOptionsPage(extension);
-    bool launched = ash::TryLaunchOsUrlHandler(url);
-    DCHECK(launched);
-  } else {
-    extensions::ExtensionTabUtil::OpenOptionsPage(
-        extension, chrome::FindBrowserWithTab(web_ui()->GetWebContents()));
-  }
+  extensions::ExtensionTabUtil::OpenOptionsPage(
+      extension, chrome::FindBrowserWithTab(web_ui()->GetWebContents()));
 }
 
 void AccessibilityHandler::MaybeAddSodaInstallerObserver() {
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 227f3ad..48fe6f7 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
@@ -1024,8 +1024,6 @@
        IDS_SETTINGS_AUDIO_INPUT_NOISE_CANCELLATION_TITLE},
       {"audioInputStyleTransferTitle",
        IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_TITLE},
-      {"audioInputStyleTransferBadge",
-       IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_BADGE},
       {"audioInputStyleTransferDescription",
        IDS_SETTINGS_AUDIO_INPUT_STYLE_TRANSFER_DESCRIPTION},
       {"audioInputTitle", IDS_SETTINGS_AUDIO_INPUT_TITLE},
diff --git a/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc b/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc
index f8d5e448..2586868 100644
--- a/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc
+++ b/chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc
@@ -21,7 +21,8 @@
 #if BUILDFLAG(ENABLE_COMPOSE)
 #include "chrome/browser/ui/webui/compose/compose_untrusted_ui.h"
 #endif  // BUILDFLAG(ENABLE_COMPOSE)
-#include "chrome/browser/ui/lens/lens_untrusted_ui_config.h"
+#include "chrome/browser/ui/lens/lens_overlay_untrusted_ui.h"
+#include "chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h"
 #endif  // defined(TOOLKIT_VIEWS)
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -46,7 +47,10 @@
 #if defined(TOOLKIT_VIEWS)
   map.AddUntrustedWebUIConfig(
       std::make_unique<CompanionSidePanelUntrustedUIConfig>());
-  map.AddUntrustedWebUIConfig(std::make_unique<lens::LensUntrustedUIConfig>());
+  map.AddUntrustedWebUIConfig(
+      std::make_unique<lens::LensOverlayUntrustedUIConfig>());
+  map.AddUntrustedWebUIConfig(
+      std::make_unique<lens::LensSidePanelUntrustedUIConfig>());
   map.AddUntrustedWebUIConfig(
       std::make_unique<ReadAnythingUIUntrustedConfig>());
   map.AddUntrustedWebUIConfig(std::make_unique<HatsUIConfig>());
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 6fb878b..2e6060d 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -711,7 +711,7 @@
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES)
-  if (url.host_piece() == chrome::kChromeUILensHost) {
+  if (url.host_piece() == chrome::kChromeUILensOverlayHost) {
     return &NewWebUI<LensUI>;
   }
 #endif
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index a6f3bf8c..edc8d5d 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -400,7 +400,7 @@
   const GURL kRequestUrl("https://supervised-user-verification.example.net");
   return std::make_unique<SupervisedUserVerificationPage>(
       web_contents, "first.last@gmail.com", kRequestUrl,
-      SupervisedUserVerificationPage::VerificationPurpose::BLOCKED_SITE,
+      SupervisedUserVerificationPage::VerificationPurpose::DEFAULT_BLOCKED_SITE,
       /*child_account_service*/ nullptr, ukm::kInvalidSourceId,
       std::make_unique<SupervisedUserVerificationControllerClient>(
           web_contents,
diff --git a/chrome/browser/ui/webui/lens/lens_ui.cc b/chrome/browser/ui/webui/lens/lens_ui.cc
index 7c3c474..2d230029 100644
--- a/chrome/browser/ui/webui/lens/lens_ui.cc
+++ b/chrome/browser/ui/webui/lens/lens_ui.cc
@@ -25,8 +25,8 @@
   // Set up Content Security Policy (CSP) for chrome-untrusted://lens iframe.
   web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme);
   // Allow chrome-untrusted://lens page to load as an iframe in the page.
-  std::string frame_src = base::StringPrintf("frame-src %s 'self';",
-                                             chrome::kChromeUILensUntrustedURL);
+  std::string frame_src = base::StringPrintf(
+      "frame-src %s 'self';", chrome::kChromeUILensOverlayUntrustedURL);
   html_source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::FrameSrc, frame_src);
 }
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 8c096711..dbdc1fc2 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -68,7 +68,7 @@
 #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/profiles/profile_helper.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chrome/browser/ui/webui/ash/extended_updates/extended_updates_dialog.h"
 #include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
 #include "chrome/browser/ui/webui/help/version_updater_chromeos.h"
diff --git a/chrome/browser/ui/webui/settings/about_handler.h b/chrome/browser/ui/webui/settings/about_handler.h
index a0c6ef7..83ba9af 100644
--- a/chrome/browser/ui/webui/settings/about_handler.h
+++ b/chrome/browser/ui/webui/settings/about_handler.h
@@ -22,7 +22,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "base/task/cancelable_task_tracker.h"
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace base {
diff --git a/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc b/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
index be4b6a5..3cae2e6 100644
--- a/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
+++ b/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
@@ -14,7 +14,7 @@
 #include "components/policy/core/common/management/management_service.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ash/tpm_firmware_update.h"
+#include "chrome/browser/ash/tpm/tpm_firmware_update.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/pref_names.h"
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 fc7e3089..7901909 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
@@ -238,6 +238,11 @@
   page_handler_->AccessibilityEventReceived(details);
 }
 
+void ReadAnythingWebContentsObserver::AccessibilityLocationChangesReceived(
+    const std::vector<ui::AXLocationChanges>& details) {
+  page_handler_->AccessibilityLocationChangesReceived(details);
+}
+
 void ReadAnythingWebContentsObserver::PrimaryPageChanged(content::Page& page) {
   page_handler_->PrimaryPageChanged();
 }
@@ -382,6 +387,13 @@
                                     details.events);
 }
 
+void ReadAnythingUntrustedPageHandler::AccessibilityLocationChangesReceived(
+    const std::vector<ui::AXLocationChanges>& details) {
+  if (features::IsReadAnythingDocsIntegrationEnabled()) {
+    page_->AccessibilityLocationChangesReceived(details);
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // ui::AXActionHandlerObserver:
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
index 2926784..b1f7a00 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
@@ -56,6 +56,8 @@
   // content::WebContentsObserver:
   void AccessibilityEventReceived(
       const ui::AXUpdatesAndEvents& details) override;
+  void AccessibilityLocationChangesReceived(
+      const std::vector<ui::AXLocationChanges>& details) override;
   void PrimaryPageChanged(content::Page& page) override;
   void WebContentsDestroyed() override;
 
@@ -99,6 +101,8 @@
   ~ReadAnythingUntrustedPageHandler() override;
 
   void AccessibilityEventReceived(const ui::AXUpdatesAndEvents& details);
+  void AccessibilityLocationChangesReceived(
+      const std::vector<ui::AXLocationChanges>& details);
   void PrimaryPageChanged();
   void WebContentsDestroyed();
 
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
index b0fdfb1..c0ecf06 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
@@ -26,6 +26,7 @@
 #include "ui/accessibility/ax_node_id_forward.h"
 #include "ui/accessibility/ax_tree_id.h"
 #include "ui/accessibility/mojom/ax_event.mojom.h"
+#include "ui/accessibility/mojom/ax_location_changes.mojom.h"
 #include "ui/accessibility/mojom/ax_tree_id.mojom.h"
 #include "ui/accessibility/mojom/ax_tree_update.mojom.h"
 
@@ -48,6 +49,9 @@
                     const std::vector<ui::AXTreeUpdate>& updates,
                     const std::vector<ui::AXEvent>& events));
   MOCK_METHOD(void,
+              AccessibilityLocationChangesReceived,
+              (const std::vector<ui::AXLocationChanges>& details));
+  MOCK_METHOD(void,
               OnSettingsRestoredFromPrefs,
               (read_anything::mojom::LineSpacing line_spacing,
                read_anything::mojom::LetterSpacing letter_spacing,
diff --git a/chrome/browser/ui/webui/signin/managed_user_profile_notice_handler.cc b/chrome/browser/ui/webui/signin/managed_user_profile_notice_handler.cc
index 801bff47..d0e1d802 100644
--- a/chrome/browser/ui/webui/signin/managed_user_profile_notice_handler.cc
+++ b/chrome/browser/ui/webui/signin/managed_user_profile_notice_handler.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/managed_ui.h"
 #include "chrome/browser/ui/signin/signin_view_controller.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/management/management_ui_handler.h"
 #include "chrome/browser/ui/webui/signin/signin_utils.h"
 #include "chrome/browser/ui/webui/webui_util.h"
@@ -58,7 +59,9 @@
   return false;
 #else
   return base::FeatureList::IsEnabled(
-      profile_management::features::kOidcAuthProfileManagement);
+             profile_management::features::kOidcAuthProfileManagement) ||
+         base::FeatureList::IsEnabled(
+             features::kEnterpriseUpdatedProfileCreationScreen);
 #endif
 }
 
@@ -239,6 +242,13 @@
   int state = args[0].GetIfInt().value_or(0);
   CHECK_NE(state, ManagedUserProfileNoticeHandler::State::kProcessing)
       << "User should not be able to click the proceed button while processing";
+  if (state == ManagedUserProfileNoticeHandler::State::kValueProposition &&
+      IsJavascriptAllowed()) {
+    FireWebUIListener("on-state-changed",
+                      ManagedUserProfileNoticeHandler::State::kDisclosure);
+    return;
+  }
+
   if (process_user_choice_with_confirmation_callback_ &&
       state == ManagedUserProfileNoticeHandler::State::kDisclosure &&
       IsJavascriptAllowed()) {
@@ -353,8 +363,9 @@
   std::string title =
       l10n_util::GetStringUTF8(IDS_ENTERPRISE_PROFILE_WELCOME_TITLE);
   std::string subtitle;
+  std::string email;
+  std::string account_name;
   std::string enterprise_info;
-  bool show_cancel_button = true;
   ProfileAttributesEntry* entry = GetProfileEntry();
 
   switch (type_) {
@@ -410,6 +421,17 @@
                    profile_creation_required_by_policy_
                        ? IDS_ENTERPRISE_PROFILE_WELCOME_CREATE_PROFILE_BUTTON
                        : IDS_APP_CONTINUE));
+
+      AccountInfo account_info =
+          IdentityManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
+              ->FindExtendedAccountInfoByAccountId(account_id_);
+      CHECK(!account_info.IsEmpty());
+      dict.Set("continueAs", l10n_util::GetStringFUTF8(
+                                 IDS_PROFILES_DICE_WEB_ONLY_SIGNIN_BUTTON,
+                                 base::UTF8ToUTF16(account_info.given_name)));
+      dict.Set("email", base::UTF16ToUTF8(email_));
+      dict.Set("accountName", account_info.full_name);
+
 #if !BUILDFLAG(IS_CHROMEOS)
       // We apply the checkLinkDataCheckboxByDefault to true value only if the
       // link data checkbox is visible and the policy
@@ -433,7 +455,6 @@
   dict.Set("title", title);
   dict.Set("subtitle", subtitle);
   dict.Set("enterpriseInfo", enterprise_info);
-  dict.Set("showCancelButton", show_cancel_button);
 
   return dict;
 }
diff --git a/chrome/browser/ui/webui/signin/managed_user_profile_notice_handler.h b/chrome/browser/ui/webui/signin/managed_user_profile_notice_handler.h
index 301762a1..8102e73 100644
--- a/chrome/browser/ui/webui/signin/managed_user_profile_notice_handler.h
+++ b/chrome/browser/ui/webui/signin/managed_user_profile_notice_handler.h
@@ -45,6 +45,7 @@
     kSuccess = 2,
     kTimeout = 3,
     kError = 4,
+    kValueProposition = 5
   };
   ManagedUserProfileNoticeHandler(
       Browser* browser,
diff --git a/chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.cc b/chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.cc
index 31e8f43..bfac009 100644
--- a/chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.cc
+++ b/chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.cc
@@ -60,6 +60,12 @@
        IDR_SIGNIN_MANAGED_USER_PROFILE_NOTICE_MANAGED_USER_PROFILE_NOTICE_DISCLOSURE_JS},
       {"managed_user_profile_notice_state.css.js",
        IDR_SIGNIN_MANAGED_USER_PROFILE_NOTICE_MANAGED_USER_PROFILE_NOTICE_STATE_CSS_JS},
+      {"managed_user_profile_notice_value_prop.css.js",
+       IDR_SIGNIN_MANAGED_USER_PROFILE_NOTICE_MANAGED_USER_PROFILE_NOTICE_VALUE_PROP_CSS_JS},
+      {"managed_user_profile_notice_value_prop.html.js",
+       IDR_SIGNIN_MANAGED_USER_PROFILE_NOTICE_MANAGED_USER_PROFILE_NOTICE_VALUE_PROP_HTML_JS},
+      {"managed_user_profile_notice_value_prop.js",
+       IDR_SIGNIN_MANAGED_USER_PROFILE_NOTICE_MANAGED_USER_PROFILE_NOTICE_VALUE_PROP_JS},
       {"managed_user_profile_notice_state.html.js",
        IDR_SIGNIN_MANAGED_USER_PROFILE_NOTICE_MANAGED_USER_PROFILE_NOTICE_STATE_HTML_JS},
       {"managed_user_profile_notice_state.js",
@@ -106,6 +112,9 @@
   source->AddLocalizedString("enterpriseProfileWelcomeTitle",
                              IDS_ENTERPRISE_PROFILE_WELCOME_TITLE);
   source->AddLocalizedString("cancelLabel", IDS_CANCEL);
+  source->AddLocalizedString(
+      "cancelValueProp",
+      IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_DECLINE_TEXT);
   source->AddLocalizedString("continueLabel", IDS_APP_CONTINUE);
   source->AddLocalizedString("confirmLabel", IDS_CONFIRM);
   source->AddLocalizedString("linkDataText",
@@ -141,12 +150,20 @@
                              IDS_ENTERPRISE_OIDC_WELCOME_ERROR_TITLE);
   source->AddLocalizedString("errorSubtitle",
                              IDS_ENTERPRISE_OIDC_WELCOME_ERROR_SUBTITLE);
+  source->AddLocalizedString(
+      "signinIntoChrome",
+      IDS_AVATAR_BUTTON_INTERCEPT_BUBBLE_CHROME_SIGNIN_TEXT);
+  source->AddLocalizedString("valuePropSubtitle",
+                             IDS_ENTERPRISE_VALUE_PROPOSITION_SUBTITLE);
 
   source->AddBoolean("useUpdatedUi",
                      base::FeatureList::IsEnabled(
                          features::kEnterpriseUpdatedProfileCreationScreen));
   source->AddBoolean("showLinkDataCheckbox", false);
   source->AddBoolean("isModalDialog", false);
+  source->AddBoolean("enforcedByPolicy", false);
+  source->AddInteger("initialState",
+                     ManagedUserProfileNoticeHandler::State::kValueProposition);
 }
 
 ManagedUserProfileNoticeUI::~ManagedUserProfileNoticeUI() = default;
@@ -177,7 +194,12 @@
                     l10n_util::GetStringUTF16(title_id));
 
     update_data.Set("showLinkDataCheckbox", show_link_data_option);
+    update_data.Set("initialState",
+                    ManagedUserProfileNoticeHandler::State::kValueProposition);
+    update_data.Set("enforcedByPolicy", profile_creation_required_by_policy);
   } else if (type == ManagedUserProfileNoticeUI::ScreenType::kEnterpriseOIDC) {
+    update_data.Set("initialState",
+                    ManagedUserProfileNoticeHandler::State::kDisclosure);
     update_data.Set("isModalDialog", true);
     update_data.Set(
         "enterpriseProfileWelcomeTitle",
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
index deffe2c..25f52e12 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -565,8 +565,7 @@
         ProfilePicker::SwitchToReauth(
             profile,
             base::BindOnce(&ProfilePickerHandler::DisplayForceSigninErrorDialog,
-                           weak_factory_.GetWeakPtr(), profile->GetPath(),
-                           base::UTF16ToUTF8(entry->GetUserName())));
+                           weak_factory_.GetWeakPtr(), profile->GetPath()));
       } else {
         ProfilePickerForceSigninDialog::ShowReauthDialog(
             profile, base::UTF16ToUTF8(entry->GetUserName()));
@@ -579,9 +578,9 @@
       // signed in with an email address matched RestrictSigninToPattern policy
       // already.
       if (base::FeatureList::IsEnabled(kForceSigninFlowInProfilePicker)) {
-        DisplayForceSigninErrorDialog(/*profile_path=*/base::FilePath(),
-                                      /*email=*/std::string(),
-                                      ReauthUIError::kNotAllowed);
+        DisplayForceSigninErrorDialog(
+            /*profile_path=*/base::FilePath(),
+            ForceSigninUIError::ReauthNotAllowed());
       } else {
         LoginUIServiceFactory::GetForProfile(profile)
             ->SetProfileBlockingErrorMessage();
@@ -612,11 +611,10 @@
 
 void ProfilePickerHandler::DisplayForceSigninErrorDialog(
     const base::FilePath& profile_path,
-    const std::string& email,
-    ReauthUIError error) {
+    const ForceSigninUIError& error) {
   AllowJavascript();
 
-  const auto& [title, body] = GetReauthUIErrorMessages(error, email);
+  const auto& [title, body] = error.GetErrorTexts();
   FireWebUIListener("display-force-signin-error-dialog", base::Value(title),
                     base::Value(body),
                     base::Value(profile_path.AsUTF16Unsafe()));
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.h b/chrome/browser/ui/webui/signin/profile_picker_handler.h
index 4bf65ca..b56bb72 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.h
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.h
@@ -39,7 +39,7 @@
 
 class Browser;
 
-enum class ReauthUIError;
+class ForceSigninUIError;
 
 // The handler for Javascript messages related to the profile picker main view.
 class ProfilePickerHandler : public content::WebUIMessageHandler,
@@ -164,20 +164,18 @@
   // Displays either a sign-in or an error dialog within the profile picker
   // using `profile`.
   void OnProfileForDialogLoaded(Profile* profile);
-  // Displays an error dialog on top of the profile picker based on the error
-  // enum.
-  // Empty `profile_path` will not show an additional "Sign in"
-  // button that allows to reach reauth step.
-  // The `email` value is only used when the error is
-  // `ReauthUIError::kWrongAccount` in order to show the expected email address.
-  void DisplayForceSigninErrorDialog(const base::FilePath& profile_path,
-                                     const std::string& email,
-                                     ReauthUIError error);
 
   // Updates if guest mode is available following a profile addition, removal,
   // or changed supervision status.
   void MaybeUpdateGuestMode();
 
+  // Displays an error dialog on top of the profile picker based on the error
+  // enum.
+  // Empty `profile_path` will not show an additional "Sign in" button that
+  // allows to reach reauth step.
+  void DisplayForceSigninErrorDialog(const base::FilePath& profile_path,
+                                     const ForceSigninUIError& error);
+
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   // GaiaId as input string.
   using AddAccountCallback = base::OnceCallback<void(const std::string&)>;
diff --git a/chrome/browser/ui/webui/signin/signin_ui_error.cc b/chrome/browser/ui/webui/signin/signin_ui_error.cc
index a9702886..8307e000 100644
--- a/chrome/browser/ui/webui/signin/signin_ui_error.cc
+++ b/chrome/browser/ui/webui/signin/signin_ui_error.cc
@@ -17,6 +17,8 @@
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "ui/base/l10n/l10n_util.h"
 
+// ------------------------------ SigninUIError --------------------------------
+
 // static
 SigninUIError SigninUIError::Ok() {
   return SigninUIError(Type::kOk, std::string(), std::u16string());
@@ -144,30 +146,65 @@
                              const std::u16string& error_message)
     : type_(type), email_(base::UTF8ToUTF16(email)), message_(error_message) {}
 
-ReauthUIErrorMessages GetReauthUIErrorMessages(ReauthUIError error,
-                                               const std::string& email) {
-  CHECK_NE(error, ReauthUIError::kNone);
-  switch (error) {
-    case ReauthUIError::kNotAllowed:
+// ---------------------------- ForceSigninUIError -----------------------------
+
+ForceSigninUIError::ForceSigninUIError(const ForceSigninUIError& other) =
+    default;
+ForceSigninUIError& ForceSigninUIError::operator=(
+    const ForceSigninUIError& other) = default;
+
+// static
+ForceSigninUIError ForceSigninUIError::ErrorNone() {
+  return ForceSigninUIError(Type::kNone, std::string());
+}
+
+// static
+ForceSigninUIError ForceSigninUIError::ReauthNotAllowed() {
+  return ForceSigninUIError(Type::kReauthNotAllowed, std::string());
+}
+
+// static
+ForceSigninUIError ForceSigninUIError::ReauthWrongAccount(
+    const std::string& email) {
+  CHECK(!email.empty());
+  return ForceSigninUIError(Type::kReauthWrongAccount, email);
+}
+
+// static
+ForceSigninUIError ForceSigninUIError::ReauthTimeout() {
+  return ForceSigninUIError(Type::kReauthTimeout, std::string());
+}
+
+ForceSigninUIError::UiTexts ForceSigninUIError::GetErrorTexts() const {
+  CHECK_NE(type_, Type::kNone);
+  switch (type_) {
+    case Type::kReauthNotAllowed:
       return {
           l10n_util::GetStringUTF16(
               IDS_PROFILE_PICKER_FORCE_SIGN_IN_ERROR_DIALOG_NOT_ALLOWED_TITLE),
           l10n_util::GetStringUTF16(
               IDS_PROFILE_PICKER_FORCE_SIGN_IN_ERROR_DIALOG_NOT_ALLOWED_BODY)};
-    case ReauthUIError::kWrongAccount:
-      CHECK(!email.empty());
+    case Type::kReauthWrongAccount:
+      CHECK(!email_.empty());
       return {
           l10n_util::GetStringUTF16(
               IDS_PROFILE_PICKER_FORCE_SIGN_IN_ERROR_DIALOG_WRONG_ACCOUNT_TITLE),
           l10n_util::GetStringFUTF16(
               IDS_PROFILE_PICKER_FORCE_SIGN_IN_ERROR_DIALOG_WRONG_ACCOUNT_BODY,
-              base::UTF8ToUTF16(email))};
-    case ReauthUIError::kTimeout:
+              base::UTF8ToUTF16(email_))};
+    case Type::kReauthTimeout:
       return {l10n_util::GetStringUTF16(
                   IDS_PROFILE_PICKER_FORCE_SIGN_IN_ERROR_TIMEOUT_TITLE),
               l10n_util::GetStringUTF16(
                   IDS_PROFILE_PICKER_FORCE_SIGN_IN_ERROR_TIMEOUT_BODY)};
-    case ReauthUIError::kNone:
+    case Type::kNone:
       NOTREACHED();
   }
 }
+
+ForceSigninUIError::Type ForceSigninUIError::type() const {
+  return type_;
+}
+
+ForceSigninUIError::ForceSigninUIError(Type type, const std::string& email)
+    : type_(type), email_(email) {}
diff --git a/chrome/browser/ui/webui/signin/signin_ui_error.h b/chrome/browser/ui/webui/signin/signin_ui_error.h
index 10ad079..9e6c001 100644
--- a/chrome/browser/ui/webui/signin/signin_ui_error.h
+++ b/chrome/browser/ui/webui/signin/signin_ui_error.h
@@ -101,28 +101,47 @@
 #endif
 };
 
-// Different UIs that can be displayed when trying to reauth while Force Signin
-// is enabled.
-enum class ReauthUIError {
-  kNone,
-  // Reauth is not allowed for this Profile.
-  kNotAllowed,
-  // Reauth was attempted using a different account than the main one.
-  kWrongAccount,
-  // A timeout occurred while attempting to reauth.
-  kTimeout,
+// Holds different errors to be displayed through the Force Signin error dialog.
+class ForceSigninUIError {
+ public:
+  ForceSigninUIError(const ForceSigninUIError& other);
+  ForceSigninUIError& operator=(const ForceSigninUIError& other);
+
+  enum class Type {
+    kNone,
+    // Reauth is not allowed for this Profile.
+    kReauthNotAllowed,
+    // Reauth was attempted using a different account than the main one.
+    // The expected email will be shown in the error message.
+    kReauthWrongAccount,
+    // A timeout occurred while attempting to reauth.
+    kReauthTimeout,
+  };
+
+  // Helper pair to get the error messages based on the `Type` error enum to be
+  // displayed on the Profile Picker error dialog.
+  // - `first` for the title/header message.
+  // - `second` for the body message.
+  using UiTexts = std::pair<std::u16string, std::u16string>;
+
+  // Static constructors for each error type with the corresponding needed
+  // inputs.
+  static ForceSigninUIError ErrorNone();
+  static ForceSigninUIError ReauthNotAllowed();
+  static ForceSigninUIError ReauthWrongAccount(const std::string& email);
+  static ForceSigninUIError ReauthTimeout();
+
+  // Returns the error messages for the given `error`.
+  // `type_` must not be `ForceSigninUIError::kNone`.
+  UiTexts GetErrorTexts() const;
+
+  Type type() const;
+
+ private:
+  ForceSigninUIError(Type type, const std::string& email);
+
+  Type type_;
+  std::string email_;
 };
 
-// Helper pair to get the error messages based on the `ReauthUIError`
-// error enum to be displayed on the Profile Picker error dialog.
-// - `first` for the title message.
-// - `second` for the body message.
-using ReauthUIErrorMessages = std::pair<std::u16string, std::u16string>;
-
-// Returns the error messages for the given `error`.
-// The `email` is only used when the error is `ReauthUIError::kWrongAccount` and
-// must not be empty in this case. `error` must not be `ReauthUIError::kNone`.
-ReauthUIErrorMessages GetReauthUIErrorMessages(ReauthUIError error,
-                                               const std::string& email);
-
 #endif  // CHROME_BROWSER_UI_WEBUI_SIGNIN_SIGNIN_UI_ERROR_H_
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 eaccaf0..6a0613f9 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
@@ -23,7 +23,6 @@
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/favicon/favicon_utils.h"
 #include "chrome/browser/feedback/show_feedback_page.h"
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_ui.cc b/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
index a17f5c8..9144d906 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
@@ -34,6 +34,7 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "ui/base/accelerators/accelerator.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/webui/color_change_listener/color_change_handler.h"
@@ -195,10 +196,15 @@
   ui::Accelerator accelerator(ui::VKEY_A,
                               ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR);
   source->AddString("shortcutText", accelerator.GetShortcutText());
+  // TODO(b/362269642): Once the stale threshold duration is Finch-
+  // configurable, replace the hardcoded 7 below with the value of that
+  // parameter.
+  source->AddString("declutterBody",
+                    l10n_util::GetStringFUTF16(IDS_DECLUTTER_BODY, u"7")),
 
-  webui::SetupWebUIDataSource(
-      source, base::make_span(kTabSearchResources, kTabSearchResourcesSize),
-      IDR_TAB_SEARCH_TAB_SEARCH_HTML);
+      webui::SetupWebUIDataSource(
+          source, base::make_span(kTabSearchResources, kTabSearchResourcesSize),
+          IDR_TAB_SEARCH_TAB_SEARCH_HTML);
 
   content::URLDataSource::Add(
       profile, std::make_unique<FaviconSource>(
diff --git a/chrome/browser/ui/webui/top_chrome/webui_url_utils.cc b/chrome/browser/ui/webui/top_chrome/webui_url_utils.cc
index c622da8..d4850ee9 100644
--- a/chrome/browser/ui/webui/top_chrome/webui_url_utils.cc
+++ b/chrome/browser/ui/webui/top_chrome/webui_url_utils.cc
@@ -18,7 +18,7 @@
   // TODO(b/339037968): Remove this exception once Lens uses ".top-chrome"
   // suffix.
 #if !BUILDFLAG(IS_ANDROID)
-  if (url == GURL(chrome::kChromeUILensUntrustedURL)) {
+  if (url == GURL(chrome::kChromeUILensOverlayUntrustedURL)) {
     return true;
   }
 #endif
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index a58cb15..516b15d1 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -1119,12 +1119,12 @@
     deps += [
       "//chrome/browser/ash/app_list",
       "//chrome/browser/ash/login:test_support",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/policy/core",
       "//chrome/browser/ash/policy/core:test_support",
       "//chrome/browser/ash/policy/test_support",
       "//chrome/browser/ash/system_web_apps/test_support",
       "//chrome/browser/ash/system_web_apps/test_support:test_support_ui",
+      "//chrome/browser/ui/ash/login",
     ]
   }
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
index 40bd9f46f..43e2c4e 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager_ash_browsertest.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/ash/login/session/user_session_manager.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/core/device_local_account_policy_service.h"
@@ -43,6 +42,7 @@
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
diff --git a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.cc b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.cc
index d0996fc84..8c32bfc 100644
--- a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.cc
@@ -113,6 +113,14 @@
 
 UpdateChannelId::UpdateChannelId(std::string id) : id_(std::move(id)) {}
 
+UpdateChannelId::UpdateChannelId(const UpdateChannelId&) = default;
+
+UpdateChannelId::UpdateChannelId(UpdateChannelId&&) = default;
+
+UpdateChannelId& UpdateChannelId::operator=(const UpdateChannelId&) = default;
+
+UpdateChannelId& UpdateChannelId::operator=(UpdateChannelId&&) = default;
+
 UpdateChannelId::~UpdateChannelId() = default;
 
 bool UpdateChannelId::operator==(const UpdateChannelId& other) const = default;
diff --git a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.h b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.h
index 55f65b95..46ae96df 100644
--- a/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.h
+++ b/chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest.h
@@ -39,6 +39,11 @@
   static base::expected<UpdateChannelId, absl::monostate> Create(
       std::string input);
 
+  UpdateChannelId(const UpdateChannelId&);
+  UpdateChannelId(UpdateChannelId&&);
+  UpdateChannelId& operator=(const UpdateChannelId&);
+  UpdateChannelId& operator=(UpdateChannelId&&);
+
   ~UpdateChannelId();
 
   bool operator==(const UpdateChannelId& other) const;
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 2c6e032..96b4e95 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1726062765-d529003e27a4b539e4c9c4c87f38786613ea1a54-c766f21701469c2e442740f5b78c4520b1bf7257.profdata
+chrome-mac-arm-main-1726106240-441bf2deff7e95460da6ac51157b879ddde4364e-1610da6495a18d6747d08ca5b067f486e138f231.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 257d5c97..e7d74503 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1726055922-1cc3fb0e3853fd94ca1749aa9e054b70b73c9407-70741de6a5fd5e903887867c5c8b7b3dbce11def.profdata
+chrome-mac-main-1726098914-f97e5bfd6939a3340b58fed166f919ad84d1055f-b3b7e88332342e29650c393287cdd32d1c19dd03.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index e1c2890..6dcb1af 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1726055922-4e22bdfb3f0d47f0adc708da2255cdb102af16b5-70741de6a5fd5e903887867c5c8b7b3dbce11def.profdata
+chrome-win32-main-1726098914-44273cc0986f0834e1f79fd63e14d66f69db1e52-b3b7e88332342e29650c393287cdd32d1c19dd03.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 707247e7..ca7f41d85 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1726055922-0ae1b2c0eff9bd12aae123d7c4504775f89ee3af-70741de6a5fd5e903887867c5c8b7b3dbce11def.profdata
+chrome-win64-main-1726098914-30ec3c56b52f62ece28772f32ce2289ad8f19a92-b3b7e88332342e29650c393287cdd32d1c19dd03.profdata
diff --git a/chrome/common/accessibility/BUILD.gn b/chrome/common/accessibility/BUILD.gn
index 67b77a72..5dd333a 100644
--- a/chrome/common/accessibility/BUILD.gn
+++ b/chrome/common/accessibility/BUILD.gn
@@ -10,6 +10,7 @@
   sources = [ "read_anything.mojom" ]
   public_deps = [
     "//mojo/public/mojom/base",
+    "//ui/accessibility:ax_features_mojo",
     "//ui/accessibility/mojom",
     "//url/mojom:url_mojom_gurl",
   ]
diff --git a/chrome/common/accessibility/read_anything.mojom b/chrome/common/accessibility/read_anything.mojom
index 65b67bd..80da845 100644
--- a/chrome/common/accessibility/read_anything.mojom
+++ b/chrome/common/accessibility/read_anything.mojom
@@ -8,7 +8,9 @@
 
 import "skia/public/mojom/skcolor.mojom";
 import "skia/public/mojom/bitmap.mojom";
+import "ui/accessibility/ax_features.mojom";
 import "ui/accessibility/mojom/ax_event.mojom";
+import "ui/accessibility/mojom/ax_location_changes.mojom";
 import "ui/accessibility/mojom/ax_tree_id.mojom";
 import "ui/accessibility/mojom/ax_tree_update.mojom";
 import "mojo/public/mojom/base/values.mojom";
@@ -215,6 +217,11 @@
                              array<ax.mojom.AXTreeUpdate> updates,
                              array<ax.mojom.AXEvent> events);
 
+  // Send a location change notification to the WebUI. The WebUI updates the
+  // locations for each node in the vector. This does not trigger a distillation.
+  [RuntimeFeature=ax.mojom.features.kReadAnythingDocsIntegration]
+  AccessibilityLocationChangesReceived(array<ax.mojom.AXLocationChanges> details);
+
   // Sends the active AXTreeID to the WebUI. This is not guaranteed to be called
   // after the tree_id was previously passed to AccessibilityEventsReceived.
   // ukm_source_id is a ukm::SourceId which is used to tie the UKM event to the
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 8b7a9f3..f2a44af 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -639,7 +639,7 @@
       "webui_untrusted"
     ],
     "matches": [
-      "chrome-untrusted://lens/*"
+      "chrome-untrusted://lens-overlay/*"
     ]
   }],
   "login": [{
diff --git a/chrome/common/extensions/api/accessibility_private.json b/chrome/common/extensions/api/accessibility_private.json
index c79b522..0742ca19 100644
--- a/chrome/common/extensions/api/accessibility_private.json
+++ b/chrome/common/extensions/api/accessibility_private.json
@@ -738,7 +738,7 @@
           {
             "name": "useRewriters",
             "type": "boolean",
-            "description": "If true, uses rewriters for the key event; only allowed if used from Dictation. Otherwise indicates that rewriters should be skipped.",
+            "description": "If true, uses rewriters for the key event; only allowed if used from Dictation or FaceGaze. Otherwise indicates that rewriters should be skipped.",
             "optional": true
           }
         ]
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 077fc30..a02f7c7 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -335,9 +335,14 @@
 inline constexpr char kChromeUIHistoryClustersSidePanelURL[] =
     "chrome://history-clusters-side-panel.top-chrome/";
 inline constexpr char kChromeUILensHost[] = "lens";
-inline constexpr char kChromeUILensUntrustedSidePanelURL[] =
+inline constexpr char kChromeUILensSidePanelHost[] = "lens";
+inline constexpr char kChromeUILensUntrustedSidePanelAPIURL[] =
     "chrome-untrusted://lens/side_panel/side_panel.html";
-inline constexpr char kChromeUILensUntrustedURL[] = "chrome-untrusted://lens/";
+inline constexpr char kChromeUILensUntrustedSidePanelURL[] =
+    "chrome-untrusted://lens/";
+inline constexpr char kChromeUILensOverlayHost[] = "lens-overlay";
+inline constexpr char kChromeUILensOverlayUntrustedURL[] =
+    "chrome-untrusted://lens-overlay/";
 inline constexpr char kChromeUINearbyInternalsHost[] = "nearby-internals";
 inline constexpr char kChromeUINearbyInternalsURL[] =
     "chrome://nearby-internals";
diff --git a/chrome/enterprise_companion/BUILD.gn b/chrome/enterprise_companion/BUILD.gn
index 1e69a6f2..f3e0521 100644
--- a/chrome/enterprise_companion/BUILD.gn
+++ b/chrome/enterprise_companion/BUILD.gn
@@ -413,9 +413,13 @@
   if (is_linux) {
     inputs = [ "$root_build_dir/enterprise_companion" ]
     deps = [ ":enterprise_companion" ]
-  } else if (is_mac && host_os == "mac") {
-    inputs = [ "$root_build_dir/enterprise_companion_installer_unsigned.pkg" ]
-    deps = [ "//chrome/enterprise_companion/mac:enterprise_companion_installer_unsigned" ]
+  } else if (is_mac) {
+    inputs = [
+      "$root_build_dir/${enterprise_companion_product_full_name}.app/Contents/Info.plist",
+      "$root_build_dir/${enterprise_companion_product_full_name}.app/Contents/MacOS/${enterprise_companion_product_full_name}",
+      "$root_build_dir/${enterprise_companion_product_full_name}.app/Contents/PkgInfo",
+    ]
+    deps = [ "//chrome/enterprise_companion/mac:enterprise_companion_bundle" ]
   } else if (is_win) {
     inputs = [ "$root_build_dir/enterprise_companion.exe" ]
     deps = [ ":enterprise_companion" ]
diff --git a/chrome/enterprise_companion/installer_win.cc b/chrome/enterprise_companion/installer_win.cc
index fafea8a..1899195 100644
--- a/chrome/enterprise_companion/installer_win.cc
+++ b/chrome/enterprise_companion/installer_win.cc
@@ -113,10 +113,10 @@
   // Try deleting the directory 15 times and wait one second between tries.
   const std::wstring command_line = base::StrCat(
       {L"\"", cmd_exe_path.value(),
-       L"\" /Q /C for /L \%G IN (1,1,15) do ( ping -n 2 127.0.0.1 > nul & "
+       L"\" /Q /C \"for /L \%G IN (1,1,15) do ( ping -n 2 127.0.0.1 > nul & "
        L"rmdir \"",
        install_directory->value(), L"\" /s /q > nul & if not exist \"",
-       install_directory->value(), L"\" exit 0 ) & exit 3"});
+       install_directory->value(), L"\" exit 0 )\""});
   VLOG(1) << "Running " << command_line;
 
   base::LaunchOptions options;
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn
index 51f7c97..ce94b6fb 100644
--- a/chrome/installer/linux/BUILD.gn
+++ b/chrome/installer/linux/BUILD.gn
@@ -453,6 +453,8 @@
       deb_arch = "mips64el"
     } else if (current_cpu == "loong64") {
       deb_arch = "loong64"
+    } else if (current_cpu == "riscv64") {
+      deb_arch = "riscv64"
     } else {
       assert(false, "Linux installer not configured for this architecture.")
     }
@@ -504,6 +506,8 @@
         rpm_arch = "mips64el"
       } else if (current_cpu == "loong64") {
         rpm_arch = "loongarch64"
+      } else if (current_cpu == "riscv64") {
+        rpm_arch = "riscv64"
       } else {
         assert(false, "Linux installer not configured for this architecture.")
       }
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index d869e47d..f5c9702 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -196,7 +196,7 @@
     "//components/safe_browsing/core/common",
     "//components/safe_browsing/core/common:interfaces",
     "//components/search:search",
-    "//components/security_interstitials/content/renderer:security_interstitial_page_controller",
+    "//components/security_interstitials/content/renderer",
     "//components/security_interstitials/core:",
     "//components/security_interstitials/core/common/mojom:",
     "//components/spellcheck:buildflags",
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 5265466..4f0cc669 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -65,6 +65,7 @@
   "+components/safe_browsing/content/renderer",
   "+components/safe_browsing/content/common",
   "+components/safe_browsing/core/common",
+  "+components/security_interstitials/content/renderer",
   "+components/signin/public/base/signin_buildflags.h",
   "+components/signin/public/base/signin_switches.h",
   "+components/spellcheck/renderer",
diff --git a/chrome/renderer/accessibility/read_aloud_app_model.cc b/chrome/renderer/accessibility/read_aloud_app_model.cc
index 3b45734..3f263695 100644
--- a/chrome/renderer/accessibility/read_aloud_app_model.cc
+++ b/chrome/renderer/accessibility/read_aloud_app_model.cc
@@ -6,6 +6,7 @@
 
 #include "base/values.h"
 #include "chrome/renderer/accessibility/read_anything_node_utils.h"
+#include "ui/accessibility/accessibility_features.h"
 
 ReadAloudAppModel::ReadAloudAppModel()
     : sentence_movement_options_(ui::AXMovementOptions(
@@ -86,7 +87,9 @@
       // out of the content- should we reset the state?
       return next_granularity.node_ids;
     }
-
+    if (features::IsReadAnythingReadAloudPhraseHighlightingEnabled()) {
+      next_granularity.CalculatePhrases();
+    }
     processed_granularities_on_current_page_.push_back(next_granularity);
   }
 
@@ -102,6 +105,9 @@
       GetNextNodes(is_pdf, is_docs, current_nodes);
 
   while (current_granularity.node_ids.size() > 0) {
+    if (features::IsReadAnythingReadAloudPhraseHighlightingEnabled()) {
+      current_granularity.CalculatePhrases();
+    }
     processed_granularities_on_current_page_.push_back(current_granularity);
     current_granularity = GetNextNodes(is_pdf, is_docs, current_nodes);
   }
@@ -526,7 +532,8 @@
 }
 
 std::vector<ReadAloudTextSegment>
-ReadAloudAppModel::GetHighlightForCurrentSegmentIndex(int index) const {
+ReadAloudAppModel::GetHighlightForCurrentSegmentIndex(int index,
+                                                      bool phrases) const {
   // If the granularity index isn't valid, return an empty array.
   if (processed_granularity_index_ >=
       processed_granularities_on_current_page_.size()) {
@@ -541,15 +548,33 @@
     return {};
   }
 
-  // Get the remaining text in the current granularity that occurs after the
-  // starting index.
-  std::u16string current_text = current_granularity.text.substr(index);
+  if (phrases && features::IsReadAnythingReadAloudPhraseHighlightingEnabled()) {
+    // Phrase highlighting. Find the previous and next boundaries, and get all
+    // segments between them.
+    int start = current_granularity.GetPhraseIndex(index);
+    if (start < 0) {
+      // Index is not valid, or phrase boundaries are not valid.
+      return {};
+    }
+    int start_index = current_granularity.phrase_boundaries[start];
+    // The phrase ends either at the start of the next phrase, or if it is the
+    // last phrase of the sentence, at the end of the sentence.
+    int end_index =
+        (start < (int)current_granularity.phrase_boundaries.size() - 1)
+            ? current_granularity.phrase_boundaries[start + 1]
+            : current_granularity.text.size();
+    return current_granularity.GetSegmentsForRange(start_index, end_index);
+  } else {
+    // Word highlighting. Get the remaining text in the current granularity that
+    // occurs after the starting index.
+    std::u16string current_text = current_granularity.text.substr(index);
 
-  // Get the word length of the next word following the index.
-  int word_length = GetNextWord(current_text);
-  int end_index = index + word_length;
+    // Get the word length of the next word following the index.
+    int word_length = GetNextWord(current_text);
+    int end_index = index + word_length;
 
-  return current_granularity.GetSegmentsForRange(index, end_index);
+    return current_granularity.GetSegmentsForRange(index, end_index);
+  }
 }
 
 void ReadAloudAppModel::IncrementMetric(const std::string& metric_name) {
diff --git a/chrome/renderer/accessibility/read_aloud_app_model.h b/chrome/renderer/accessibility/read_aloud_app_model.h
index 89bb53c..f5662cd0 100644
--- a/chrome/renderer/accessibility/read_aloud_app_model.h
+++ b/chrome/renderer/accessibility/read_aloud_app_model.h
@@ -118,7 +118,9 @@
 
   // Given a text index for the current granularity, return the nodes and the
   // corresponding text ranges for that part of the text. The text ranges
-  // consist of start and end offsets within each node.
+  // consist of start and end offsets within each node. If the `phrases`
+  // argument is `true`, the text ranges for the containing phrase are returned,
+  // otherwise the text ranges for the word are returned.
   //
   // For example, if a current granularity segment has text:
   // "Hello darkness, my old friend."
@@ -131,7 +133,8 @@
   // For index=17, which corresponds to the word "my ", will return:
   //    [{"207", 6, 9}].
   std::vector<ReadAloudTextSegment> GetHighlightForCurrentSegmentIndex(
-      int index) const;
+      int index,
+      bool phrases) const;
 
   // Updates the session count for the given metric name using
   // SingleSampleMetric. These are then logged once on destruction.
diff --git a/chrome/renderer/accessibility/read_aloud_traversal_utils.cc b/chrome/renderer/accessibility/read_aloud_traversal_utils.cc
index 022aee5..6fc0a5e 100644
--- a/chrome/renderer/accessibility/read_aloud_traversal_utils.cc
+++ b/chrome/renderer/accessibility/read_aloud_traversal_utils.cc
@@ -77,6 +77,33 @@
   return ret;
 }
 
+void ReadAloudCurrentGranularity::CalculatePhrases() {
+  if (text.size() == 0) {
+    phrase_boundaries.clear();
+    return;
+  }
+
+  // Add a phrase boundary every 3 words. TODO(crbug.com/330749762): replace
+  // with the correct phrase calculation.
+  std::size_t start = 0;
+  int count = 0;
+  do {
+    if (count % 3 == 0) {
+      phrase_boundaries.push_back(start);
+    }
+    int next_word = GetNextWord(text.substr(start));
+    if (next_word == 0) {
+      break;
+    }
+    start += next_word;
+    ++count;
+    if (start >= text.size()) {
+      break;
+    }
+  } while (start);
+  phrase_boundaries.push_back(text.size());
+}
+
 }  // namespace a11y
 
 namespace {
diff --git a/chrome/renderer/accessibility/read_aloud_traversal_utils.h b/chrome/renderer/accessibility/read_aloud_traversal_utils.h
index 8ad536e..8a4fa24 100644
--- a/chrome/renderer/accessibility/read_aloud_traversal_utils.h
+++ b/chrome/renderer/accessibility/read_aloud_traversal_utils.h
@@ -54,6 +54,21 @@
                int text_end,
                const std::u16string& text);
 
+  // For a given start..end range within `text`, returns a list of nodes and
+  // offsets corresponding to that range.
+  std::vector<ReadAloudTextSegment> GetSegmentsForRange(int start_index,
+                                                        int end_index);
+
+  // Calculate phrase boundaries from the text.
+  void CalculatePhrases();
+
+  // Calculate the phrase_boundaries index corresponding to a text index.
+  int GetPhraseIndex(int index) {
+    return std::upper_bound(phrase_boundaries.begin(), phrase_boundaries.end(),
+                            index) -
+           phrase_boundaries.begin() - 1;
+  }
+
   // All of the ReadAloudTextSegments in the current granularity.
   std::map<ui::AXNodeID, ReadAloudTextSegment> segments;
 
@@ -80,10 +95,8 @@
   // highlighting.
   std::u16string text;
 
-  // For a given start..end range within `text`, returns a list of nodes and
-  // offsets corresponding to that range.
-  std::vector<ReadAloudTextSegment> GetSegmentsForRange(int start_index,
-                                                        int end_index);
+  // Boundary indices for phrases. Starts at 0.
+  std::vector<int> phrase_boundaries;
 };
 }  // namespace a11y
 
diff --git a/chrome/renderer/accessibility/read_aloud_traversal_utils_unittest.cc b/chrome/renderer/accessibility/read_aloud_traversal_utils_unittest.cc
index 4adc96dc..aab02c1 100644
--- a/chrome/renderer/accessibility/read_aloud_traversal_utils_unittest.cc
+++ b/chrome/renderer/accessibility/read_aloud_traversal_utils_unittest.cc
@@ -218,3 +218,95 @@
       current_granularity.GetSegmentsForRange(38, 45);
   EXPECT_THAT(out, IsEmpty());
 }
+
+TEST_F(ReadAnythingReadAloudTraversalUtilsTest,
+       CalculatePhrases_EmptyText_ReturnsEmptyResult) {
+  a11y::ReadAloudCurrentGranularity empty_sentence;
+
+  EXPECT_THAT(empty_sentence.phrase_boundaries, IsEmpty());
+
+  // CalculatePhrases should do nothing with an empty sentence.
+  empty_sentence.CalculatePhrases();
+
+  EXPECT_THAT(empty_sentence.phrase_boundaries, IsEmpty());
+}
+
+TEST_F(
+    ReadAnythingReadAloudTraversalUtilsTest,
+    CalculatePhrases_DifferentKindsOfText_ReturnsPhraseBoundariesEvery3Words) {
+  a11y::ReadAloudCurrentGranularity normal_sentence;
+  // Sentence and indices:
+  // Ice-cream candy and cake
+  // 012345678901234567890123
+  normal_sentence.AddText(101, 12, 36, u"Ice cream candy and cake");
+  // Before calculating phrases, phrase_boundaries is empty.
+  EXPECT_THAT(normal_sentence.phrase_boundaries, IsEmpty());
+
+  normal_sentence.CalculatePhrases();
+  // Boundaries at "I" of Ice and 'c' of cake.
+  EXPECT_THAT(normal_sentence.phrase_boundaries, ElementsAre(0, 16, 24));
+
+  a11y::ReadAloudCurrentGranularity sentence_with_hyphen;
+  // Sentence and indices:
+  // Ice-cream candy and cake
+  // 012345678901234567890123
+  sentence_with_hyphen.AddText(101, 12, 36, u"Ice-cream candy and cake");
+  sentence_with_hyphen.CalculatePhrases();
+  // Boundaries at "I" of Ice-cream and 'a' of and
+  EXPECT_THAT(sentence_with_hyphen.phrase_boundaries, ElementsAre(0, 16, 24));
+
+  // Length is a multiple of three.
+  a11y::ReadAloudCurrentGranularity sixword_sentence;
+  sixword_sentence.AddText(101, 41, 65, u"He is going to the mall.");
+  sixword_sentence.CalculatePhrases();
+  // Boundary every 3 words.
+  EXPECT_THAT(sixword_sentence.phrase_boundaries, ElementsAre(0, 12, 24));
+
+  // Short sentences.
+  a11y::ReadAloudCurrentGranularity short_sentence;
+  short_sentence.AddText(101, 12, 27, u"Ice-cream candy");
+  short_sentence.CalculatePhrases();
+  // Boundary only at "I" of Ice-cream
+  EXPECT_THAT(short_sentence.phrase_boundaries, ElementsAre(0, 15));
+
+  // Very short sentences.
+  a11y::ReadAloudCurrentGranularity oneword_sentence;
+  oneword_sentence.AddText(101, 12, 15, u"Yes");
+  oneword_sentence.CalculatePhrases();
+  EXPECT_THAT(oneword_sentence.phrase_boundaries, ElementsAre(0, 3));
+}
+
+TEST_F(ReadAnythingReadAloudTraversalUtilsTest,
+       GetPhraseIndex_ReturnsCorrectIndexes) {
+  a11y::ReadAloudCurrentGranularity current_granularity;
+  // Sentence and indices:
+  // Ice-cream candy and cakes and yummy treats for all of us, except you!
+  // 0123456789012345678901234567890123456789012345678901234567890123456789
+  current_granularity.AddText(101, 12, 22, u"Ice-cream ");
+  current_granularity.AddText(102, 40, 56, u"candy and cakes ");
+  current_granularity.AddText(103, 7, 28, u"and yummy treats for ");
+  current_granularity.AddText(104, 16, 26, u"all of us,");
+  current_granularity.AddText(105, 20, 31, u"except you!");
+
+  current_granularity.phrase_boundaries = {0, 26, 43, 58, 69};
+  // Before the start of the string returns -1.
+  EXPECT_THAT(current_granularity.GetPhraseIndex(-1), -1);
+
+  // Within the string, returns the closest phrase start.
+  EXPECT_THAT(current_granularity.GetPhraseIndex(0), 0);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(1), 0);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(10), 0);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(25), 0);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(26), 1);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(30), 1);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(42), 1);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(43), 2);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(57), 2);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(58), 3);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(68), 3);
+
+  // Past the end of the string returns the last index.
+  EXPECT_THAT(current_granularity.GetPhraseIndex(69), 4);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(79), 4);
+  EXPECT_THAT(current_granularity.GetPhraseIndex(109), 4);
+}
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index fe78a8b4..ff2c5c1 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -524,6 +524,21 @@
   }
 }
 
+void ReadAnythingAppController::AccessibilityLocationChangesReceived(
+    const std::vector<ui::AXLocationChanges>& details) {
+  // Listen to location change notifications to update locations of the nodes
+  // accordingly.
+  for (auto& change : details) {
+    ui::AXNode* ax_node = model_.GetAXNode(change.id);
+    if (!ax_node) {
+      continue;
+    }
+    ax_node->SetLocation(change.new_location.offset_container_id,
+                         change.new_location.bounds,
+                         change.new_location.transform.get());
+  }
+}
+
 void ReadAnythingAppController::ExecuteJavaScript(const std::string& script) {
   // TODO(b/1266555): Use v8::Function rather than javascript. If possible,
   // replace this function call with firing an event.
@@ -1791,13 +1806,14 @@
 }
 
 v8::Local<v8::Value>
-ReadAnythingAppController::GetHighlightForCurrentSegmentIndex(int index) {
+ReadAnythingAppController::GetHighlightForCurrentSegmentIndex(int index,
+                                                              bool phrases) {
   v8::Isolate* isolate =
       render_frame()->GetWebFrame()->GetAgentGroupScheduler()->Isolate();
   auto context = isolate->GetCurrentContext();
 
   std::vector<ReadAloudTextSegment> nodes =
-      read_aloud_model_.GetHighlightForCurrentSegmentIndex(index);
+      read_aloud_model_.GetHighlightForCurrentSegmentIndex(index, phrases);
 
   v8::Local<v8::Array> highlight_array = v8::Array::New(isolate, nodes.size());
   for (int i = 0; i < (int)nodes.size(); i++) {
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything_app_controller.h
index 0475e6d..4c601c3 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything_app_controller.h
@@ -112,6 +112,8 @@
       const ui::AXTreeID& tree_id,
       const std::vector<ui::AXTreeUpdate>& updates,
       const std::vector<ui::AXEvent>& events) override;
+  void AccessibilityLocationChangesReceived(
+      const std::vector<ui::AXLocationChanges>& details) override;
   void OnActiveAXTreeIDChanged(const ui::AXTreeID& tree_id,
                                ukm::SourceId ukm_source_id,
                                bool is_pdf) override;
@@ -322,10 +324,14 @@
   // correctly position the highlight within the current text segment. The
   // return value is thus a list containing node id, start, and length.
   //
+  //  If the `phrases` argument is `true`, the text ranges for the containing
+  //  phrase are returned, otherwise the text ranges for the word are returned.
+  //
   // Note that this is only needed for custom granularity highlighting. Sentence
   // highlighting is able to be handled directly in WebUI because the entire
   // speech segment is highlighted at once.
-  v8::Local<v8::Value> GetHighlightForCurrentSegmentIndex(int index);
+  v8::Local<v8::Value> GetHighlightForCurrentSegmentIndex(int index,
+                                                          bool phrases);
 
   // SetContentForTesting and SetLanguageForTesting are used by
   // ReadAnythingAppTest and thus need to be kept in ReadAnythingAppController
diff --git a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
index 5a5d524..48e882e9 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
@@ -252,6 +252,11 @@
     controller_->AccessibilityEventReceived(tree_id, updates, events);
   }
 
+  void AccessibilityLocationChangesReceived(
+      const std::vector<ui::AXLocationChanges>& details) {
+    controller_->AccessibilityLocationChangesReceived(details);
+  }
+
   // Since a11y events happen asynchronously, they can come between the time
   // distillation finishes and pending updates are unserialized in
   // OnAXTreeDistilled. Thus we need to be able to set distillation progress
@@ -516,10 +521,12 @@
         controller_->model_.is_pdf(), controller_->model_.IsDocs(),
         &controller_->model_.display_node_ids());
   }
+
   std::vector<ReadAloudTextSegment> GetHighlightForCurrentSegmentIndex(
-      int index) {
+      int index,
+      bool phrase) {
     return controller_->read_aloud_model_.GetHighlightForCurrentSegmentIndex(
-        index);
+        index, phrase);
   }
 
   std::vector<ui::AXNodeID> GetCurrentText(
@@ -573,6 +580,14 @@
     InitAXPosition(nodes[0].id);
   }
 
+  bool IsPhraseHighlightingEnabled() {
+    return controller_->IsPhraseHighlightingEnabled();
+  }
+
+  ui::AXNodeData GetNodeData(ui::AXNodeID ax_node_id) {
+    return controller_->model_.GetAXNode(ax_node_id)->data();
+  }
+
   ui::AXTreeID tree_id_;
   raw_ptr<MockAXTreeDistiller, DanglingUntriaged> distiller_ = nullptr;
   testing::StrictMock<MockReadAnythingUntrustedPageHandler> page_handler_;
@@ -1659,6 +1674,43 @@
   EXPECT_EQ(u"Node 4", GetTextContent(4));
 }
 
+TEST_F(ReadAnythingAppControllerTest, AccessibilityLocationChangesReceived) {
+  ui::AXTreeUpdate update;
+  ui::AXTreeID id_1 = ui::AXTreeID::CreateNewAXTreeID();
+  test::SetUpdateTreeID(&update, id_1);
+
+  ui::AXRelativeBounds initial_bounds;
+  initial_bounds.bounds = gfx::RectF(1, 1, 100, 100);
+  initial_bounds.offset_container_id = 12345;
+  ui::AXNodeData node;
+  node.id = 2;
+  node.relative_bounds = initial_bounds;
+
+  ui::AXNodeData root;
+  root.id = 1;
+  root.child_ids = {node.id};
+  update.nodes = {root, node};
+  update.root_id = root.id;
+
+  AccessibilityEventReceived({update});
+  OnAXTreeDistilled({1});
+  OnActiveAXTreeIDChanged(id_1);
+
+  // Create a new bounding box that the node will update to have
+  ui::AXRelativeBounds location_update;
+  location_update.offset_container_id = 1;
+  location_update.bounds = gfx::RectF(5, 5, 100, 100);
+  ui::AXLocationChanges location_changes;
+  location_changes.id = 2;
+  location_changes.ax_tree_id = id_1;
+  location_changes.new_location = location_update;
+
+  // Test that the node data updates correctly
+  AccessibilityLocationChangesReceived({location_changes});
+  node = GetNodeData(2);
+  EXPECT_EQ(node.relative_bounds, location_update);
+}
+
 TEST_F(ReadAnythingAppControllerTest, OnActiveAXTreeIDChanged) {
   // Create three AXTreeUpdates with three different tree IDs.
   std::vector<ui::AXTreeID> tree_ids = {ui::AXTreeID::CreateNewAXTreeID(),
@@ -3937,7 +3989,7 @@
 
   // Before there are any processed granularities, GetHighlightStartIndex
   // should return an invalid id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1, false), IsEmpty());
 
   std::vector<ui::AXNodeID> node_ids = GetCurrentText();
   EXPECT_EQ((int)node_ids.size(), 1);
@@ -3947,15 +3999,16 @@
 
   // Since we just have one node with one text segment, the returned index
   // should equal the passed parameter.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text.id, 0, 4)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(3),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(3, false),
               ElementsAre(TextSegmentMatcher(static_text.id, 3, 4)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7, false),
               ElementsAre(TextSegmentMatcher(static_text.id, 7, 13)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence_length - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence_length - 1, false),
               ElementsAre(TextSegmentMatcher(static_text.id, 21, 22)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence_length), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence_length, false),
+              IsEmpty());
 }
 
 TEST_F(
@@ -3974,54 +4027,57 @@
 
   // Before there are any processed granularities,
   // GetHighlightForCurrentSegmentIndex should return an empty array.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1, false), IsEmpty());
 
   std::vector<ui::AXNodeID> node_ids = GetCurrentText();
   EXPECT_EQ((int)node_ids.size(), 3);
 
   // Spot check that indices 0->sentence1.length() map to the first node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 0, 6)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 7, 11)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() - 1, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 16, 17)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length()),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length(), false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 0, 3)));
 
   // Spot check that indices in sentence 2 map to the second node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() + 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() + 1, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 1, 3)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(26),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(26, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 9, 15)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() +
-                                                 sentence2.length() - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length() - 1, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 14, 15)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() +
-                                                 sentence2.length()),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length(), false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 0, 3)));
 
   // Spot check that indices in sentence 3 map to the third node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() +
-                                                 sentence2.length() + 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length() + 1, false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 1, 3)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(40),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(40, false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 8, 11)));
   EXPECT_THAT(
       GetHighlightForCurrentSegmentIndex(
-          sentence1.length() + sentence2.length() + sentence3.length() - 1),
+          sentence1.length() + sentence2.length() + sentence3.length() - 1,
+          false),
       ElementsAre(TextSegmentMatcher(static_text3.id, 30, 31)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
-                  sentence1.length() + sentence2.length() + sentence3.length()),
-              IsEmpty());
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex(
+          sentence1.length() + sentence2.length() + sentence3.length(), false),
+      IsEmpty());
 
   // Out-of-bounds nodes return an empty array.
   EXPECT_THAT(
       GetHighlightForCurrentSegmentIndex(
-          sentence1.length() + sentence2.length() + sentence3.length() + 1),
+          sentence1.length() + sentence2.length() + sentence3.length() + 1,
+          false),
       IsEmpty());
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(535), IsEmpty());
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(-10), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(535, false), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(-10, false), IsEmpty());
 }
 
 TEST_F(
@@ -4046,8 +4102,8 @@
 
   // Before there are any processed granularities, GetHighlightStartIndex
   // should return an invalid id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1), IsEmpty());
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1, false), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1, false), IsEmpty());
 
   std::vector<ui::AXNodeID> node_ids = GetCurrentText();
   EXPECT_EQ((int)node_ids.size(), 1);
@@ -4061,16 +4117,17 @@
 
   // For the first node in the first segment, the returned index should equal
   // the passed parameter.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 0, 4)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(6),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(6, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 6, 11)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(15),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(15, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 15, 16)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment1_length - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment1_length - 1, false),
               ElementsAre(TextSegmentMatcher(
                   static_text1.id, segment1_length - 1, segment1_length)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment1_length), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment1_length, false),
+              IsEmpty());
 
   // Move to segment 2.
   node_ids = MoveToNextGranularityAndGetText();
@@ -4079,24 +4136,24 @@
   // For the second segment, the boundary index will have reset for the new
   // speech segment. The correct highlight start index is the index that the
   // boundary index within the segment corresponds to within the node.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, segment1_length,
                                              segment1_length + 6)));
   EXPECT_THAT(
-      GetHighlightForCurrentSegmentIndex(10),
+      GetHighlightForCurrentSegmentIndex(10, false),
       ElementsAre(TextSegmentMatcher(static_text1.id, segment1_length + 10,
                                      segment1_length + 12)));
   EXPECT_THAT(
-      GetHighlightForCurrentSegmentIndex(13),
+      GetHighlightForCurrentSegmentIndex(13, false),
       ElementsAre(TextSegmentMatcher(static_text1.id, segment1_length + 13,
                                      segment1_length + 18)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment2_length - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment2_length - 1, false),
               ElementsAre(TextSegmentMatcher(
                   static_text1.id, segment1_length + segment2_length - 1,
                   segment1_length + segment2_length)));
-  EXPECT_THAT(
-      GetHighlightForCurrentSegmentIndex(segment1_length + segment2_length),
-      IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  segment1_length + segment2_length, false),
+              IsEmpty());
 
   // Move to segment 3.
   node_ids = MoveToNextGranularityAndGetText();
@@ -4105,29 +4162,29 @@
   // For the third segment, the boundary index will have reset for the new
   // speech segment. The correct highlight start index is the index that the
   // boundary index within the segment corresponds to within the node.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(
                   static_text1.id, segment1_length + segment2_length,
                   segment1_length + segment2_length + 3)));
 
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(9),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(9, false),
               ElementsAre(TextSegmentMatcher(
                   static_text1.id, segment1_length + segment2_length + 9,
                   segment1_length + segment2_length + 15)));
 
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(13),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(13, false),
               ElementsAre(TextSegmentMatcher(
                   static_text1.id, segment1_length + segment2_length + 13,
                   segment1_length + segment2_length + 15)));
 
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment3_length - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment3_length - 1, false),
               ElementsAre(TextSegmentMatcher(
                   static_text1.id,
                   segment1_length + segment2_length + segment3_length - 1,
                   segment1_length + segment2_length + segment3_length)));
 
   EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
-                  segment1_length + segment2_length + segment3_length),
+                  segment1_length + segment2_length + segment3_length, false),
               IsEmpty());
 
   // Move to segment 4.
@@ -4140,49 +4197,54 @@
   // the correct highlight start corresponds to the index within the first
   // node.
   EXPECT_THAT(
-      GetHighlightForCurrentSegmentIndex(0),
+      GetHighlightForCurrentSegmentIndex(0, false),
       ElementsAre(TextSegmentMatcher(
           static_text1.id, segment1_length + segment2_length + segment3_length,
           segment1_length + segment2_length + segment3_length + 4)));
 
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(2),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(2, false),
               ElementsAre(TextSegmentMatcher(
                   static_text1.id,
                   segment1_length + segment2_length + segment3_length + 2,
                   segment1_length + segment2_length + segment3_length + 4)));
 
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(8),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(8, false),
               ElementsAre(TextSegmentMatcher(
                   static_text1.id,
                   segment1_length + segment2_length + segment3_length + 8,
                   segment1_length + segment2_length + segment3_length + 17)));
 
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment4_partial_length - 1),
-              ElementsAre(TextSegmentMatcher(
-                  static_text1.id,
-                  segment1_length + segment2_length + segment3_length +
-                      segment4_partial_length - 1,
-                  segment1_length + segment2_length + segment3_length +
-                      segment4_partial_length)));
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex(segment4_partial_length - 1, false),
+      ElementsAre(
+          TextSegmentMatcher(static_text1.id,
+                             segment1_length + segment2_length +
+                                 segment3_length + segment4_partial_length - 1,
+                             segment1_length + segment2_length +
+                                 segment3_length + segment4_partial_length)));
 
   EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
                   segment1_length + segment2_length + segment3_length +
-                  segment4_partial_length),
+                      segment4_partial_length,
+                  false),
               IsEmpty());
 
   // For the second node, the highlight index corresponds to the position within
   // the second node.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment4_partial_length),
-              ElementsAre(TextSegmentMatcher(static_text2.id, 0, 5)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment4_partial_length + 2),
-              ElementsAre(TextSegmentMatcher(static_text2.id, 2, 5)));
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex(segment4_partial_length, false),
+      ElementsAre(TextSegmentMatcher(static_text2.id, 0, 5)));
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex(segment4_partial_length + 2, false),
+      ElementsAre(TextSegmentMatcher(static_text2.id, 2, 5)));
 
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment4_full_length - 1),
-              ElementsAre(TextSegmentMatcher(static_text2.id,
-                                             (int)node2_text.length() - 1,
-                                             (int)node2_text.length())));
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex(segment4_full_length - 1, false),
+      ElementsAre(TextSegmentMatcher(static_text2.id,
+                                     (int)node2_text.length() - 1,
+                                     (int)node2_text.length())));
 
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment4_full_length),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(segment4_full_length, false),
               IsEmpty());
 }
 
@@ -4201,19 +4263,19 @@
 
   // Before there are any processed granularities,
   // GetNodeIdForCurrentSegmentIndex should return an invalid id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1, false), IsEmpty());
 
   std::vector<ui::AXNodeID> node_ids = GetCurrentText();
   EXPECT_EQ((int)node_ids.size(), 1);
 
   // Spot check that indices 0->sentence1.length() map to the first node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 0, 6)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 7, 11)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() - 1, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 31, 32)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length()),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length(), false),
               IsEmpty());
 
   // Move to the next granularity.
@@ -4221,25 +4283,25 @@
   EXPECT_EQ((int)node_ids.size(), 2);
 
   // Spot check that indices in sentence 2 map to the second node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 0, 3)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 7, 11)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence2.length() - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence2.length() - 1, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 20, 21)));
 
   // Spot check that indices in sentence 3 map to the third node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence2.length() + 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence2.length() + 1, false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 1, 10)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(27),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(27, false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 6, 10)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence2.length() +
-                                                 sentence3.length() - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence2.length() + sentence3.length() - 1, false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 9, 10)));
 
   // Out-of-bounds nodes return invalid.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence2.length() +
-                                                 sentence3.length() + 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence2.length() + sentence3.length() + 1, false),
               IsEmpty());
 }
 
@@ -4258,7 +4320,7 @@
 
   // Before there are any processed granularities,
   // GetNodeIdForCurrentSegmentIndex should return an invalid id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1, false), IsEmpty());
 
   std::vector<ui::AXNodeID> node_ids = GetCurrentText();
   EXPECT_EQ((int)node_ids.size(), 2);
@@ -4268,11 +4330,11 @@
   EXPECT_EQ((int)node_ids.size(), 1);
 
   // Spot check that indices 0->sentence3.length() map to the third node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 0, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7, false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 7, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence3.length() - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence3.length() - 1, false),
               ElementsAre(TextSegmentMatcher(static_text3.id, 36, 37)));
 
   // Move backwards.
@@ -4280,25 +4342,25 @@
   EXPECT_EQ((int)node_ids.size(), 2);
 
   // Spot check that indices in sentence 1 map to the first node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 0, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(6),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(6, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 6, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() - 1, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 23, 24)));
 
   // Spot check that indices in sentence 2 map to the second node id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() + 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() + 1, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 1, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(27),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(27, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 3, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() +
-                                                 sentence2.length() - 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length() - 1, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 38, 39)));
 
   // Out-of-bounds nodes return invalid.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() +
-                                                 sentence2.length() + 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length() + 1, false),
               IsEmpty());
 }
 
@@ -4321,57 +4383,136 @@
 
   // Before there are any processed granularities,
   // GetNodeIdForCurrentSegmentIndex should return an invalid id.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(1, false), IsEmpty());
 
   std::vector<ui::AXNodeID> node_ids = GetCurrentText();
   EXPECT_EQ((int)node_ids.size(), 2);
 
   // Throughout first word.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 0, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(2),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(2, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 2, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex((int)word1.length() - 2),
-              ElementsAre(TextSegmentMatcher(static_text1.id, 6, 8)));
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex((int)word1.length() - 2, false),
+      ElementsAre(TextSegmentMatcher(static_text1.id, 6, 8)));
 
   // Throughout third word.
   int third_word_index = sentence1.find(word3);
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(third_word_index),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(third_word_index, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 12, 17)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(third_word_index + 2),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(third_word_index + 2, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 14, 17)));
 
   // Words split across node boundaries
   int sixth_word_index = sentence1.find(word6);
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sixth_word_index),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sixth_word_index, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 26, 29),
                           TextSegmentMatcher(static_text2.id, 0, 4)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sixth_word_index + 2),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sixth_word_index + 2, false),
               ElementsAre(TextSegmentMatcher(static_text1.id, 28, 29),
                           TextSegmentMatcher(static_text2.id, 0, 4)));
 
   int seventh_word_index = sentence1.length();
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(seventh_word_index),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(seventh_word_index, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 0, 4)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(seventh_word_index + 2),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(seventh_word_index + 2, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 2, 4)));
 
   int last_word_index = sentence1.length() + sentence2.find(word8);
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(last_word_index),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(last_word_index, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 4, 8)));
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(last_word_index + 2),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(last_word_index + 2, false),
               ElementsAre(TextSegmentMatcher(static_text2.id, 6, 8)));
 
   // Boundary testing.
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(-5), IsEmpty());
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() +
-                                                 sentence2.length()),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(-5, false), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length(), false),
               IsEmpty());
-  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() +
-                                                 sentence2.length() + 1),
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length() + 1, false),
               IsEmpty());
 }
 
+TEST_F(
+    ReadAnythingAppControllerTest,
+    GetHighlightForCurrentSegmentIndex_PhrasesEnabled_SentenceSpansMultipleNodes_ReturnsCorrectNodes) {
+  scoped_feature_list_.InitWithFeatures(
+      {features::kReadAnythingReadAloud,
+       features::kReadAnythingReadAloudAutomaticWordHighlighting,
+       features::kReadAnythingReadAloudPhraseHighlighting},
+      {});
+
+  EXPECT_TRUE(IsPhraseHighlightingEnabled());
+  // Text indices:             0123456789012345678901234567890
+  std::u16string sentence1 = u"Never feel heavy ";
+  std::u16string sentence2 = u"or earthbound, ";
+  std::u16string sentence3 = u"no worries or doubts interfere.";
+
+  ui::AXNodeData static_text1 = test::TextNode(/* id= */ 2, sentence1);
+  ui::AXNodeData static_text2 = test::TextNode(/* id= */ 3, sentence2);
+  ui::AXNodeData static_text3 = test::TextNode(/* id= */ 4, sentence3);
+
+  InitializeWithAndProcessNodes({static_text1, static_text2, static_text3});
+
+  std::vector<ui::AXNodeID> node_ids = GetCurrentText();
+  EXPECT_EQ((int)node_ids.size(), 3);
+
+  // Spot check that indices 0->sentence1.length() map to the first node id.
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(0, true),
+              ElementsAre(TextSegmentMatcher(static_text1.id, 0, 17)));
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(7, true),
+              ElementsAre(TextSegmentMatcher(static_text1.id, 0, 17)));
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() - 1, true),
+              ElementsAre(TextSegmentMatcher(static_text1.id, 0, 17)));
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length(), true),
+              ElementsAre(TextSegmentMatcher(static_text2.id, 0, 15),
+                          TextSegmentMatcher(static_text3.id, 0, 3)));
+
+  // Spot check that indices in sentence 2 map to the second node id.
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(sentence1.length() + 1, true),
+              ElementsAre(TextSegmentMatcher(static_text2.id, 0, 15),
+                          TextSegmentMatcher(static_text3.id, 0, 3)));
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(26, true),
+              ElementsAre(TextSegmentMatcher(static_text2.id, 0, 15),
+                          TextSegmentMatcher(static_text3.id, 0, 3)));
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length() - 1, true),
+              ElementsAre(TextSegmentMatcher(static_text2.id, 0, 15),
+                          TextSegmentMatcher(static_text3.id, 0, 3)));
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length(), true),
+              ElementsAre(TextSegmentMatcher(static_text2.id, 0, 15),
+                          TextSegmentMatcher(static_text3.id, 0, 3)));
+
+  // Spot check that indices in sentence 3 map to the third node id.
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(
+                  sentence1.length() + sentence2.length() + 1, true),
+              ElementsAre(TextSegmentMatcher(static_text2.id, 0, 15),
+                          TextSegmentMatcher(static_text3.id, 0, 3)));
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(40, true),
+              ElementsAre(TextSegmentMatcher(static_text3.id, 3, 21)));
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex(
+          sentence1.length() + sentence2.length() + sentence3.length() - 1,
+          true),
+      ElementsAre(TextSegmentMatcher(static_text3.id, 21, 31)));
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex(
+          sentence1.length() + sentence2.length() + sentence3.length(), true),
+      IsEmpty());
+
+  // Out-of-bounds nodes return an empty array.
+  EXPECT_THAT(
+      GetHighlightForCurrentSegmentIndex(
+          sentence1.length() + sentence2.length() + sentence3.length() + 1,
+          true),
+      IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(535, true), IsEmpty());
+  EXPECT_THAT(GetHighlightForCurrentSegmentIndex(-10, true), IsEmpty());
+}
+
 class ReadAnythingAppControllerScreen2xDataCollectionModeTest
     : public ReadAnythingAppControllerTest {
  public:
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 2ed484e9..8a3cf8d6 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -108,6 +108,7 @@
 #include "components/permissions/features.h"
 #include "components/safe_browsing/buildflags.h"
 #include "components/safe_browsing/content/renderer/threat_dom_details.h"
+#include "components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "components/subresource_filter/content/renderer/subresource_filter_agent.h"
 #include "components/subresource_filter/content/renderer/unverified_ruleset_dealer.h"
@@ -659,6 +660,9 @@
 
   new NetErrorHelper(render_frame);
 
+  new security_interstitials::SecurityInterstitialPageControllerDelegateImpl(
+      render_frame);
+
   new SupervisedUserErrorPageControllerDelegateImpl(render_frame);
 
   if (!render_frame->IsMainFrame()) {
@@ -1368,6 +1372,9 @@
           http_method == "POST", std::move(alternative_error_page_info),
           error_html);
 
+  security_interstitials::SecurityInterstitialPageControllerDelegateImpl::Get(
+      render_frame)
+      ->PrepareForErrorPage();
   SupervisedUserErrorPageControllerDelegateImpl::Get(render_frame)
       ->PrepareForErrorPage();
 }
diff --git a/chrome/renderer/net/DEPS b/chrome/renderer/net/DEPS
index 61b7ce0..2359825 100644
--- a/chrome/renderer/net/DEPS
+++ b/chrome/renderer/net/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+components/error_page/renderer",
   "+components/offline_pages/core",
-  "+components/security_interstitials/content/renderer",
   "+components/security_interstitials/core",
   "+components/security_interstitials/core/common/mojom",
   "+gin",
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc
index f6287da4..6348f3f 100644
--- a/chrome/renderer/net/net_error_helper.cc
+++ b/chrome/renderer/net/net_error_helper.cc
@@ -266,8 +266,6 @@
 }
 
 void NetErrorHelper::EnablePageHelperFunctions() {
-  security_interstitials::SecurityInterstitialPageController::Install(
-      render_frame());
   NetErrorPageController::Install(
       render_frame(), weak_controller_delegate_factory_.GetWeakPtr());
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8289fe3..8ff955c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1984,6 +1984,7 @@
       "//chrome/browser/ui/content_settings:browser_tests",
       "//chrome/browser/ui/customize_chrome",
       "//chrome/browser/ui/exclusive_access",
+      "//chrome/browser/ui/exclusive_access:browser_tests",
       "//chrome/browser/ui/find_bar:browser_tests",
       "//chrome/browser/ui/omnibox",
       "//chrome/browser/ui/page_action:icon_type",
@@ -3055,9 +3056,6 @@
       "../browser/ui/browser_navigator_iwa_browsertest.cc",
       "../browser/ui/browser_tab_strip_model_delegate_browsertest.cc",
       "../browser/ui/browser_tabrestore_browsertest.cc",
-      "../browser/ui/exclusive_access/exclusive_access_manager_browsertest.cc",
-      "../browser/ui/exclusive_access/fullscreen_controller_browsertest.cc",
-      "../browser/ui/exclusive_access/pointer_lock_controller_browsertest.cc",
       "../browser/ui/extensions/extension_enable_flow_browsertest.cc",
       "../browser/ui/extensions/extension_enable_flow_test_delegate.cc",
       "../browser/ui/extensions/extension_enable_flow_test_delegate.h",
@@ -4922,9 +4920,6 @@
         "//chrome/browser/ash/login/session:test_support",
         "//chrome/browser/ash/login/signin",
         "//chrome/browser/ash/login/test:test_support",
-        "//chrome/browser/ash/login/ui",
-        "//chrome/browser/ash/login/ui:test_support",
-        "//chrome/browser/ash/login/ui/login_screen_extension_ui",
         "//chrome/browser/ash/login/users",
         "//chrome/browser/ash/login/users/avatar:test_support",
         "//chrome/browser/ash/net",
@@ -4970,6 +4965,9 @@
         "//chrome/browser/ui/ash/holding_space:test_support",
         "//chrome/browser/ui/ash/keyboard",
         "//chrome/browser/ui/ash/keyboard:ash_test_support",
+        "//chrome/browser/ui/ash/login",
+        "//chrome/browser/ui/ash/login:test_support",
+        "//chrome/browser/ui/ash/login/login_screen_extension_ui",
         "//chrome/browser/ui/ash/multi_user",
         "//chrome/browser/ui/ash/multi_user:test_support",
         "//chrome/browser/ui/ash/network",
@@ -5176,6 +5174,7 @@
       ]
       deps += [
         "//chrome/browser/safe_browsing:metrics_collector",
+        "//chrome/browser/ui/exclusive_access:test_support",
         "//components/safe_browsing/content/browser:safe_browsing_blocking_page",
         "//components/safe_browsing/content/common:file_type_policies_test_support",
         "//components/safe_browsing/core/browser:safe_browsing_metrics_collector",
@@ -6440,7 +6439,6 @@
       "../browser/ui/download/download_bubble_row_list_view_info_unittest.cc",
       "../browser/ui/download/download_bubble_row_view_info_unittest.cc",
       "../browser/ui/download/download_bubble_security_view_info_unittest.cc",
-      "../browser/ui/exclusive_access/exclusive_access_permission_manager_unittest.cc",
       "../browser/ui/passwords/password_cross_domain_confirmation_popup_controller_impl_unittest.cc",
       "../browser/ui/plus_addresses/plus_address_creation_controller_desktop_unittest.cc",
       "../browser/ui/safety_hub/card_data_helper_unittest.cc",
@@ -7419,6 +7417,7 @@
       "../browser/android/history/history_deletion_bridge_unittest.cc",
       "../browser/android/locale/locale_template_url_loader_unittest.cc",
       "../browser/android/metrics/android_session_durations_service_unittest.cc",
+      "../browser/android/omnibox/autocomplete_controller_android_unittest.cc",
       "../browser/android/omnibox/chrome_omnibox_navigation_observer_android_unittest.cc",
       "../browser/android/oom_intervention/near_oom_monitor_unittest.cc",
       "../browser/android/oom_intervention/near_oom_reduction_message_delegate_unittest.cc",
@@ -7826,8 +7825,6 @@
       "../browser/ui/color/chrome_color_provider_utils_unittest.cc",
       "../browser/ui/color/material_new_tab_page_color_mixer_unittest.cc",
       "../browser/ui/color/new_tab_page_color_mixer_unittest.cc",
-      "../browser/ui/exclusive_access/exclusive_access_bubble_unittest.cc",
-      "../browser/ui/exclusive_access/fullscreen_controller_state_unittest.cc",
       "../browser/ui/extensions/controlled_home_bubble_delegate_unittest.cc",
       "../browser/ui/extensions/extension_action_view_controller_unittest.cc",
       "../browser/ui/extensions/extension_installed_bubble_model_unittest.cc",
@@ -8282,6 +8279,7 @@
       "//chrome/browser/ui/content_settings:unit_tests",
       "//chrome/browser/ui/cookie_controls:unit_tests",
       "//chrome/browser/ui/exclusive_access",
+      "//chrome/browser/ui/exclusive_access:unit_tests",
       "//chrome/browser/ui/lens:unit_tests",
       "//chrome/browser/ui/omnibox",
       "//chrome/browser/ui/signin",
@@ -8625,7 +8623,6 @@
       "//chrome/browser/ash/kcer",
       "//chrome/browser/ash/login/quick_unlock",
       "//chrome/browser/ash/login/smart_lock",
-      "//chrome/browser/ash/login/ui:test_support",
       "//chrome/browser/ash/login/users",
       "//chrome/browser/ash/login/users:test_support",
       "//chrome/browser/ash/login/users/avatar",
@@ -8688,6 +8685,7 @@
       "//chrome/browser/ui/ash/assistant",
       "//chrome/browser/ui/ash/holding_space",
       "//chrome/browser/ui/ash/holding_space:test_support",
+      "//chrome/browser/ui/ash/login:test_support",
       "//chrome/browser/ui/ash/multi_user",
       "//chrome/browser/ui/ash/session:test_support",
       "//chrome/browser/ui/ash/shelf",
@@ -9455,16 +9453,17 @@
 
   if (is_linux) {
     sources += [
-      "../browser/notifications/notification_platform_bridge_linux_unittest.cc",
       "../browser/shell_integration_linux_unittest.cc",
       "../browser/upgrade_detector/directory_monitor_unittest.cc",
       "../browser/upgrade_detector/get_installed_version_linux_unittest.cc",
     ]
 
-    deps += [
-      "../browser/enterprise/connectors/device_trust/key_management/installer/management_service:unit_tests",
-      "//components/dbus/thread_linux",
-    ]
+    deps += [ "../browser/enterprise/connectors/device_trust/key_management/installer/management_service:unit_tests" ]
+
+    if (use_dbus) {
+      sources += [ "../browser/notifications/notification_platform_bridge_linux_unittest.cc" ]
+      deps += [ "//components/dbus/thread_linux" ]
+    }
 
     if (use_ozone) {
       deps += [ "//ui/ozone" ]
@@ -10465,8 +10464,6 @@
       "../browser/ssl/cert_verifier_browser_test.h",
       "../browser/ssl/cert_verifier_platform_browser_test.cc",
       "../browser/ssl/cert_verifier_platform_browser_test.h",
-      "../browser/ui/exclusive_access/exclusive_access_test.cc",
-      "../browser/ui/exclusive_access/exclusive_access_test.h",
       "../browser/ui/search/ntp_test_utils.cc",
       "../browser/ui/search/ntp_test_utils.h",
       "../browser/ui/test/test_browser_closed_waiter.cc",
@@ -10897,9 +10894,6 @@
       "../browser/ssl/typed_navigation_upgrade_throttle_browsertest.cc",
       "../browser/ui/browser_command_controller_interactive_browsertest.cc",
       "../browser/ui/browser_focus_uitest.cc",
-      "../browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc",
-      "../browser/ui/exclusive_access/fullscreen_controller_state_interactive_browsertest.cc",
-      "../browser/ui/exclusive_access/fullscreen_interactive_browsertest.cc",
       "../browser/ui/fullscreen_keyboard_browsertest_base.cc",
       "../browser/ui/fullscreen_keyboard_browsertest_base.h",
       "../browser/ui/hung_renderer/hung_renderer_interactive_uitest.cc",
@@ -11039,6 +11033,7 @@
       "//chrome/browser/ui/blocked_content:interactive_ui_tests",
       "//chrome/browser/ui/browser_window",
       "//chrome/browser/ui/exclusive_access",
+      "//chrome/browser/ui/exclusive_access:interactive_ui_tests",
       "//chrome/browser/ui/find_bar:interactive_ui_tests",
       "//chrome/browser/ui/find_bar:test_support",
       "//chrome/browser/ui/lens:interactive_ui_tests",
@@ -11206,7 +11201,10 @@
         "../browser/guest_view/mime_handler_view/chrome_mime_handler_view_interactive_uitest.cc",
       ]
 
-      deps += [ "//chrome/browser/controlled_frame:test_support" ]
+      deps += [
+        "//chrome/browser/controlled_frame:test_support",
+        "//chrome/browser/ui/exclusive_access:test_support",
+      ]
 
       if (is_chromeos_ash) {
         # For ChromeVox tests.
@@ -11455,12 +11453,12 @@
           "//chrome/browser/ash/child_accounts/time_limits:test_support",
           "//chrome/browser/ash/crosapi",
           "//chrome/browser/ash/login:test_support",
-          "//chrome/browser/ash/login/ui",
           "//chrome/browser/ash/system_web_apps/apps",
           "//chrome/browser/ash/system_web_apps/test_support",
           "//chrome/browser/ash/system_web_apps/test_support:test_support_ui",
           "//chrome/browser/ui/ash",
           "//chrome/browser/ui/ash/cast_config",
+          "//chrome/browser/ui/ash/login",
           "//chrome/browser/ui/ash/multi_user",
           "//chrome/test/base/ash/interactive:test_support",
           "//chromeos/ash/components/dbus/fwupd",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/WebPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/WebPageStation.java
index aaf70833..bf083f3 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/WebPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/WebPageStation.java
@@ -14,6 +14,7 @@
 import org.chromium.base.test.transit.ConditionStatusWithResult;
 import org.chromium.base.test.transit.ConditionWithResult;
 import org.chromium.base.test.transit.Elements;
+import org.chromium.base.test.transit.Transition;
 import org.chromium.base.test.transit.UiThreadCondition;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
@@ -93,6 +94,20 @@
         return enterFacilitySync(new IncognitoWebPageAppMenuFacility(), MENU_BUTTON::click);
     }
 
+    /** Trigger to scroll WebContents to the bottom. */
+    public Transition.Trigger scrollToBottomTrigger() {
+        return () -> {
+            assertSuppliersCanBeUsed();
+            try {
+                JavaScriptUtils.executeJavaScriptAndWaitForResult(
+                        mWebContentsSupplier.get(),
+                        "window.scrollTo(0, document.body.scrollHeight)");
+            } catch (TimeoutException e) {
+                throw new RuntimeException(e);
+            }
+        };
+    }
+
     private static class WebContentsPresentCondition extends ConditionWithResult<WebContents> {
         private final Supplier<Tab> mLoadedTabSupplier;
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/TopBottomLinksPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/TopBottomLinksPageStation.java
index 7dfb126c..94280e9 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/TopBottomLinksPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/TopBottomLinksPageStation.java
@@ -9,6 +9,7 @@
 
 import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
+import org.chromium.base.test.transit.Transition;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.transit.context_menu.LinkContextMenuFacility;
 import org.chromium.chrome.test.transit.page.PageStation;
@@ -45,22 +46,25 @@
         return Pair.create(station, topFacility);
     }
 
-    // TODO(crbug.com/362995902): Make this more generic and move to WebPageStation.
-    private void scrollDown() {
-        View contentView = mActivityTabSupplier.get().getView();
-        float width = contentView.getWidth();
-        float height = contentView.getHeight();
-        // Start the scroll with some height to avoid touching the nav bar region.
-        float fromY = height - height / 10;
-        float toY = 0;
-        TouchCommon.performDragNoFling(
-                mActivityElement.get(),
-                width / 2,
-                width / 2,
-                fromY,
-                toY,
-                /* steps= */ 50,
-                /* duration= */ 500);
+    /** Scrolls down the page using a drag gesture to dismiss browser controls. */
+    private Transition.Trigger gestureScrollToBottomTrigger() {
+        return () -> {
+            assertSuppliersCanBeUsed();
+            View contentView = mActivityTabSupplier.get().getView();
+            float width = contentView.getWidth();
+            float height = contentView.getHeight();
+            // Start the scroll with some height to avoid touching the nav bar region.
+            float fromY = height - height / 10;
+            float toY = 0;
+            TouchCommon.performDragNoFling(
+                    mActivityElement.get(),
+                    width / 2,
+                    width / 2,
+                    fromY,
+                    toY,
+                    /* steps= */ 50,
+                    /* duration= */ 500);
+        };
     }
 
     /** The page is scrolled to the top, and the top link is displayed. */
@@ -83,7 +87,7 @@
         /** Scroll to the bottom of the page. */
         public BottomFacility scrollToBottom() {
             return mHostStation.swapFacilitySync(
-                    this, new BottomFacility(), mHostStation::scrollDown);
+                    this, new BottomFacility(), mHostStation.gestureScrollToBottomTrigger());
         }
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
index 85b5cad43..ff8adcc 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java
@@ -256,8 +256,13 @@
                 });
     }
 
-    /** Waits for a non-empty list of omnibox suggestions is shown. */
+    /** Waits for a non-empty list of omnibox suggestions to be shown. */
     public void checkSuggestionsShown() {
+        checkSuggestionsShown(true);
+    }
+
+    /** Waits for a non-empty list of omnibox suggestions to be {@link shown}. */
+    public void checkSuggestionsShown(boolean shown) {
         CriteriaHelper.pollUiThread(
                 () -> {
                     OmniboxSuggestionsDropdown suggestionsDropdown =
@@ -268,14 +273,25 @@
                             "suggestion list is null",
                             suggestionsDropdown,
                             Matchers.notNullValue());
-                    Criteria.checkThat(
-                            "suggestion list is not shown",
-                            suggestionsDropdown.getViewGroup().isShown(),
-                            Matchers.is(true));
-                    Criteria.checkThat(
-                            "suggestion list has no entries",
-                            suggestionsDropdown.getDropdownItemViewCountForTest(),
-                            Matchers.greaterThan(0));
+                    if (shown) {
+                        Criteria.checkThat(
+                                "suggestion list is not shown",
+                                suggestionsDropdown.getViewGroup().isShown(),
+                                Matchers.is(true));
+                        Criteria.checkThat(
+                                "suggestion list has no entries",
+                                suggestionsDropdown.getDropdownItemViewCountForTest(),
+                                Matchers.greaterThan(0));
+                    } else {
+                        Criteria.checkThat(
+                                "suggestion list is shown",
+                                suggestionsDropdown.getViewGroup().isShown(),
+                                Matchers.is(false));
+                        Criteria.checkThat(
+                                "suggestion list has entries",
+                                suggestionsDropdown.getDropdownItemViewCountForTest(),
+                                Matchers.equalTo(0));
+                    }
                 });
     }
 
diff --git a/chrome/test/base/ash/interactive/DIR_METADATA b/chrome/test/base/ash/interactive/DIR_METADATA
new file mode 100644
index 0000000..339c5f1
--- /dev/null
+++ b/chrome/test/base/ash/interactive/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1318544
+}
+team_email: "cros-connectivity@google.com"
diff --git a/chrome/test/base/ash/interactive/bluetooth/DIR_METADATA b/chrome/test/base/ash/interactive/bluetooth/DIR_METADATA
new file mode 100644
index 0000000..be64e82
--- /dev/null
+++ b/chrome/test/base/ash/interactive/bluetooth/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1131776
+}
+team_email: "cros-connectivity@google.com"
diff --git a/chrome/test/base/ash/interactive/cellular/DIR_METADATA b/chrome/test/base/ash/interactive/cellular/DIR_METADATA
new file mode 100644
index 0000000..49cea40
--- /dev/null
+++ b/chrome/test/base/ash/interactive/cellular/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1131774
+}
+team_email: "cros-connectivity@google.com"
diff --git a/chrome/test/base/ash/interactive/hotspot/DIR_METADATA b/chrome/test/base/ash/interactive/hotspot/DIR_METADATA
new file mode 100644
index 0000000..5542473
--- /dev/null
+++ b/chrome/test/base/ash/interactive/hotspot/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1281224
+}
+team_email: "cros-connectivity@google.com"
diff --git a/chrome/test/base/ash/interactive/interactive_ash_test.cc b/chrome/test/base/ash/interactive/interactive_ash_test.cc
index 73b4a2c6..88407f9e 100644
--- a/chrome/test/base/ash/interactive/interactive_ash_test.cc
+++ b/chrome/test/base/ash/interactive/interactive_ash_test.cc
@@ -63,6 +63,28 @@
   })";
 
 // This JavaScript defines a function "action" that returns `true` if `el`
+// and a sibling of the element contains the expected inner texts.
+constexpr char kFindMatchingTextsInElementAndSibling[] = R"(
+  function action(el) {
+    if (!el) {
+      return false;
+    }
+
+    var textElement = el.shadowRoot.querySelector(%s);
+    if (!textElement || textElement.innerText.indexOf(%s) == -1) {
+      return false;
+    }
+
+    var siblingElement = el.shadowRoot.querySelector(%s);
+    if (!siblingElement || siblingElement.innerText.indexOf(%s) == -1) {
+      return false;
+    }
+
+    return true;
+  }
+)";
+
+// This JavaScript defines a function "action" that returns `true` if `el`
 // contains the expected inner text. Before returning `el` is clicked.
 constexpr char kClickElementWithTextActionJs[] = R"(
   function action(el) {
@@ -706,6 +728,22 @@
 }
 
 ui::test::internal::InteractiveTestPrivate::MultiStep
+InteractiveAshTest::WaitForAnyElementAndSiblingTextContains(
+    const ui::ElementIdentifier& element_id,
+    const WebContentsInteractionTestUtil::DeepQuery& root,
+    const WebContentsInteractionTestUtil::DeepQuery& selectors,
+    const WebContentsInteractionTestUtil::DeepQuery& element_with_text,
+    const std::string& expected_text,
+    const WebContentsInteractionTestUtil::DeepQuery& sibling_element,
+    const std::string& sibling_expected_text) {
+  return FindElementAndDoActionOnChildren(
+      element_id, root, selectors,
+      FindMatchingTextsInElementAndSibling(element_with_text, expected_text,
+                                           sibling_element,
+                                           sibling_expected_text));
+}
+
+ui::test::internal::InteractiveTestPrivate::MultiStep
 InteractiveAshTest::WaitForElementHasAttribute(
     const ui::ElementIdentifier& element_id,
     WebContentsInteractionTestUtil::DeepQuery element,
@@ -951,3 +989,16 @@
                             base::GetQuotedJSONString(expected).c_str(),
                             DeepQueryToSelectors(element_to_click).c_str());
 }
+
+const std::string InteractiveAshTest::FindMatchingTextsInElementAndSibling(
+    const WebContentsInteractionTestUtil::DeepQuery& element_with_text,
+    const std::string& expected_text,
+    const WebContentsInteractionTestUtil::DeepQuery& sibling_element,
+    const std::string& sibling_expected_text) {
+  return base::StringPrintf(
+      kFindMatchingTextsInElementAndSibling,
+      DeepQueryToSelectors(element_with_text).c_str(),
+      base::GetQuotedJSONString(expected_text).c_str(),
+      DeepQueryToSelectors(sibling_element).c_str(),
+      base::GetQuotedJSONString(sibling_expected_text).c_str());
+}
diff --git a/chrome/test/base/ash/interactive/interactive_ash_test.h b/chrome/test/base/ash/interactive/interactive_ash_test.h
index e2ab912..d595f2b 100644
--- a/chrome/test/base/ash/interactive/interactive_ash_test.h
+++ b/chrome/test/base/ash/interactive/interactive_ash_test.h
@@ -278,6 +278,19 @@
       const WebContentsInteractionTestUtil::DeepQuery& selectors,
       const std::string& expected);
 
+  // This function is similar to `WaitForAnyElementTextContains()`
+  // it also checks that any sibling of the element contains a certain text
+  // `sibling_text`.
+  ui::test::internal::InteractiveTestPrivate::MultiStep
+  WaitForAnyElementAndSiblingTextContains(
+      const ui::ElementIdentifier& element_id,
+      const WebContentsInteractionTestUtil::DeepQuery& root,
+      const WebContentsInteractionTestUtil::DeepQuery& selectors,
+      const WebContentsInteractionTestUtil::DeepQuery& element_with_text,
+      const std::string& expected_text,
+      const WebContentsInteractionTestUtil::DeepQuery& sibling_element,
+      const std::string& sibling_expected_text);
+
   // Waits for an element identified by `query` to both exist in the DOM of an
   // instrumented WebUI identified by `element_id` and have attribute
   // `attribute`.
@@ -399,6 +412,15 @@
       const WebContentsInteractionTestUtil::DeepQuery& element_with_text,
       const std::string& expected,
       const WebContentsInteractionTestUtil::DeepQuery& element_to_click);
+
+  // Returns the JS code that searches for an element selected by
+  // `element_with_text` that contains the `expected_text`, and when found will
+  // check that a `sibling_element` contains text `sibling_expected_text`.
+  const std::string FindMatchingTextsInElementAndSibling(
+      const WebContentsInteractionTestUtil::DeepQuery& element_with_text,
+      const std::string& expected_text,
+      const WebContentsInteractionTestUtil::DeepQuery& sibling_element,
+      const std::string& sibling_expected_text);
 };
 
 #endif  // CHROME_TEST_BASE_ASH_INTERACTIVE_INTERACTIVE_ASH_TEST_H_
diff --git a/chrome/test/base/ash/interactive/network/shill_service_util.cc b/chrome/test/base/ash/interactive/network/shill_service_util.cc
index 67121be5..e2c207f6 100644
--- a/chrome/test/base/ash/interactive/network/shill_service_util.cc
+++ b/chrome/test/base/ash/interactive/network/shill_service_util.cc
@@ -37,6 +37,11 @@
   ShillProfileClient::Get()->GetTestInterface()->AddService(
       ShillProfileClient::GetSharedProfilePath(), service_path_);
 
+  // Marking this service as connectable since networks other than Ethernet
+  // are not connectable by default.
+  ShillServiceClient::Get()->GetTestInterface()->SetServiceProperty(
+      service_path_, shill::kConnectableProperty, base::Value(true));
+
   if (connected) {
     ConnectShillService(service_path_);
   }
diff --git a/chrome/test/base/ash/interactive/settings/interactive_uitest_elements.cc b/chrome/test/base/ash/interactive/settings/interactive_uitest_elements.cc
index 5d900a5..28055e91 100644
--- a/chrome/test/base/ash/interactive/settings/interactive_uitest_elements.cc
+++ b/chrome/test/base/ash/interactive/settings/interactive_uitest_elements.cc
@@ -485,7 +485,8 @@
 namespace wifi {
 
 WebContentsInteractionTestUtil::DeepQuery WifiNetworksList() {
-  return InternetPage() + "settings-internet-subpage" + "div#networkListDiv";
+  return InternetPage() + "settings-internet-subpage" +
+         "network-list#networkList";
 }
 
 WebContentsInteractionTestUtil::DeepQuery WifiSubpageEnableToggle() {
diff --git a/chrome/test/base/ash/interactive/vpn/DIR_METADATA b/chrome/test/base/ash/interactive/vpn/DIR_METADATA
new file mode 100644
index 0000000..2018435
--- /dev/null
+++ b/chrome/test/base/ash/interactive/vpn/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1131913
+}
+team_email: "cros-connectivity@google.com"
diff --git a/chrome/test/base/ash/interactive/wifi/DIR_METADATA b/chrome/test/base/ash/interactive/wifi/DIR_METADATA
new file mode 100644
index 0000000..f6ed9fa
--- /dev/null
+++ b/chrome/test/base/ash/interactive/wifi/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1131912
+}
+team_email: "cros-connectivity@google.com"
diff --git a/chrome/test/base/ash/interactive/wifi/wifi_interactive_uitest.cc b/chrome/test/base/ash/interactive/wifi/wifi_interactive_uitest.cc
index addbb17..f6fdda6 100644
--- a/chrome/test/base/ash/interactive/wifi/wifi_interactive_uitest.cc
+++ b/chrome/test/base/ash/interactive/wifi/wifi_interactive_uitest.cc
@@ -34,8 +34,18 @@
 
     // Set up context for element tracking for InteractiveBrowserTest.
     SetupContextWidget();
+  }
 
-    wifi_service_info_.ConfigureService(/*connected=*/true);
+  void ConfigureWifi(bool connected) {
+    wifi_service_info_.ConfigureService(connected);
+  }
+
+  const std::string WifiServicePath() const {
+    return wifi_service_info_.service_path();
+  }
+
+  const std::string WifiServiceName() const {
+    return wifi_service_info_.service_name();
   }
 
  private:
@@ -46,6 +56,8 @@
   DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ShillDevicePowerStateObserver,
                                       kWifiPoweredState);
 
+  ConfigureWifi(/*connected=*/true);
+
   // Ensure the OS Settings app is installed.
   InstallSystemApps();
 
@@ -97,6 +109,8 @@
   DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ShillDevicePowerStateObserver,
                                       kWifiPoweredState);
 
+  ConfigureWifi(/*connected=*/true);
+
   // Use a poller because the toggle gets set on a small delay, and we want to
   // avoid race conditions when checking the state.
   using ToggleObserver =
@@ -142,5 +156,65 @@
       Log("Test complete"));
 }
 
+IN_PROC_BROWSER_TEST_F(WifiInteractiveUiTest, ConnectFromSettingsSubpage) {
+  DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<bool>,
+                                      kIsWifiConnected);
+  ConfigureWifi(/*connected=*/false);
+
+  // Ensure the OS Settings app is installed.
+  InstallSystemApps();
+
+  const WebContentsInteractionTestUtil::DeepQuery kWifiNetworkItem(
+      {"network-list-item"});
+  const WebContentsInteractionTestUtil::DeepQuery kWifiItemTitle =
+      kWifiNetworkItem + "div#itemTitle";
+
+  const WebContentsInteractionTestUtil::DeepQuery kWifiItemSublabel =
+      kWifiNetworkItem + "div#sublabel";
+
+  ui::ElementContext context =
+      LaunchSystemWebApp(SystemWebAppType::SETTINGS, kOSSettingsId);
+
+  // Run the following steps with the OS Settings context set as the default.
+  RunTestSequenceInContext(
+      context,
+
+      PollState(kIsWifiConnected,
+                [this]() -> bool {
+                  const auto* shill_service_client_test =
+                      ShillServiceClient::Get()->GetTestInterface();
+                  CHECK(shill_service_client_test);
+                  const auto* wifi_properties =
+                      shill_service_client_test->GetServiceProperties(
+                          WifiServicePath());
+                  CHECK(wifi_properties);
+                  const std::string* connected =
+                      wifi_properties->FindString(shill::kStateProperty);
+                  return connected && *connected == shill::kStateOnline;
+                }),
+      WaitForState(kIsWifiConnected, false),
+
+      Log("Navigate to the WiFi subpage"),
+
+      NavigateSettingsToNetworkSubpage(kOSSettingsId,
+                                       NetworkTypePattern::WiFi()),
+      WaitForElementTextContains(
+          kOSSettingsId, settings::InternetSettingsSubpageTitle(),
+          /*text=*/l10n_util::GetStringUTF8(IDS_NETWORK_TYPE_WIFI)),
+      WaitForElementExists(kOSSettingsId, settings::wifi::WifiNetworksList()),
+
+      Log("Connect to a Wifi network"),
+
+      ClickAnyElementTextContains(kOSSettingsId,
+                                  settings::wifi::WifiNetworksList(),
+                                  kWifiItemTitle, WifiServiceName()),
+      WaitForState(kIsWifiConnected, true),
+      WaitForAnyElementAndSiblingTextContains(
+          kOSSettingsId, settings::wifi::WifiNetworksList(), kWifiNetworkItem,
+          kWifiItemTitle, WifiServiceName(), kWifiItemSublabel,
+          /*text=*/l10n_util::GetStringUTF8(IDS_ONC_CONNECTED).c_str()),
+      Log("Test complete"));
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/chrome/test/chromedriver/chrome/browser_info.cc b/chrome/test/chromedriver/chrome/browser_info.cc
index 48a10707..4a01d3cb 100644
--- a/chrome/test/chromedriver/chrome/browser_info.cc
+++ b/chrome/test/chromedriver/chrome/browser_info.cc
@@ -25,8 +25,12 @@
 
 BrowserInfo::BrowserInfo(const BrowserInfo&) = default;
 
+BrowserInfo::BrowserInfo(BrowserInfo&&) = default;
+
 BrowserInfo& BrowserInfo::operator=(const BrowserInfo&) = default;
 
+BrowserInfo& BrowserInfo::operator=(BrowserInfo&&) = default;
+
 Status BrowserInfo::FillFromBrowserVersionResponse(
     const base::Value::Dict& response) {
   const std::string* browser_string = response.FindString("product");
diff --git a/chrome/test/chromedriver/chrome/browser_info.h b/chrome/test/chromedriver/chrome/browser_info.h
index 6c57dfa0..dddceb60 100644
--- a/chrome/test/chromedriver/chrome/browser_info.h
+++ b/chrome/test/chromedriver/chrome/browser_info.h
@@ -20,7 +20,9 @@
 struct BrowserInfo {
   BrowserInfo();
   BrowserInfo(const BrowserInfo&);
+  BrowserInfo(BrowserInfo&&);
   BrowserInfo& operator=(const BrowserInfo&);
+  BrowserInfo& operator=(BrowserInfo&&);
   ~BrowserInfo();
 
   std::string android_package;
diff --git a/chrome/test/chromedriver/chrome/mobile_device.cc b/chrome/test/chromedriver/chrome/mobile_device.cc
index 8989fd9..841b5b5 100644
--- a/chrome/test/chromedriver/chrome/mobile_device.cc
+++ b/chrome/test/chromedriver/chrome/mobile_device.cc
@@ -168,10 +168,17 @@
 }  // namespace
 
 MobileDevice::MobileDevice() = default;
+
 MobileDevice::MobileDevice(const MobileDevice&) = default;
-MobileDevice::~MobileDevice() = default;
+
+MobileDevice::MobileDevice(MobileDevice&&) = default;
+
 MobileDevice& MobileDevice::operator=(const MobileDevice&) = default;
 
+MobileDevice& MobileDevice::operator=(MobileDevice&&) = default;
+
+MobileDevice::~MobileDevice() = default;
+
 Status MobileDevice::FindMobileDevice(std::string device_name,
                                       MobileDevice* mobile_device) {
   auto parsed_json = base::JSONReader::ReadAndReturnValueWithError(
diff --git a/chrome/test/chromedriver/chrome/mobile_device.h b/chrome/test/chromedriver/chrome/mobile_device.h
index 83c89f57..613bde8 100644
--- a/chrome/test/chromedriver/chrome/mobile_device.h
+++ b/chrome/test/chromedriver/chrome/mobile_device.h
@@ -17,8 +17,11 @@
 struct MobileDevice {
   MobileDevice();
   MobileDevice(const MobileDevice&);
-  ~MobileDevice();
+  MobileDevice(MobileDevice&&);
   MobileDevice& operator=(const MobileDevice&);
+  MobileDevice& operator=(MobileDevice&&);
+  ~MobileDevice();
+
   // Returns the reduced User-agent string for
   // https://github.com/WICG/ua-client-hints.
   Status GetReducedUserAgent(std::string major_version,
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 4c1d2e36..cff39a60 100644
--- a/chrome/test/data/extensions/api_test/cookies/api/background.js
+++ b/chrome/test/data/extensions/api_test/cookies/api/background.js
@@ -819,19 +819,70 @@
     // unpartitioned cookie being set.
     let emptyKey = structuredClone(TEST_PARTITIONED_COOKIE);
     emptyKey.partitionKey = {};
-    chrome.cookies.set(TEST_UNPARTITIONED_COOKIE, pass(function(cookie) {
+    emptyKey.value = 'emptyValue';
+    chrome.cookies.set(emptyKey, pass(function(cookie) {
                          chrome.test.assertEq(
                              cookie.value,
-                             TEST_UNPARTITIONED_COOKIE.value,
+                             emptyKey.value,
                          );
                          chrome.test.assertEq(
                              cookie.partitionKey,
                              TEST_UNPARTITIONED_COOKIE.partitionKey);
-                         chrome.cookies.remove({
-                           name: TEST_UNPARTITIONED_COOKIE.name,
-                           url: TEST_UNPARTITIONED_COOKIE.url
-                         });
+                         chrome.cookies.remove(
+                             {name: emptyKey.name, url: emptyKey.url});
                        }));
+
+    // Confirm that setting a cookie with an no `hasCrossSiteAncestor` but a
+    // `url` and `toplevel` site that are third party results in the value being
+    // correctly populated by the browser.
+    const thirdPartyAncestor = structuredClone(TEST_PARTITIONED_COOKIE);
+    thirdPartyAncestor.partitionKey = {
+      topLevelSite: TEST_PARTITIONED_COOKIE.partitionKey.topLevelSite
+    };
+    thirdPartyAncestor.value = 'thirdPartyAncestor';
+    thirdPartyAncestor.partitionKey = {topLevelSite: 'https://notcookies.com'};
+    chrome.cookies.set(
+        thirdPartyAncestor, pass(function(cookie) {
+          chrome.test.assertEq(
+              cookie.value,
+              thirdPartyAncestor.value,
+          );
+          const expectedKey = {
+            topLevelSite: thirdPartyAncestor.partitionKey.topLevelSite,
+            hasCrossSiteAncestor: true
+          };
+          chrome.test.assertEq(cookie.partitionKey, expectedKey);
+          chrome.cookies.remove({
+            name: thirdPartyAncestor.name,
+            url: thirdPartyAncestor.url,
+            partitionKey: expectedKey
+          });
+        }));
+
+    // Confirm that setting a cookie with an no `hasCrossSiteAncestor` but a
+    // `url` and `toplevel` site that are first party results in the value being
+    // correctly populated by the browser.
+    const firstPartyAncestor =
+        structuredClone(TEST_FIRST_PARTY_PARTITIONED_COOKIE);
+    firstPartyAncestor.value = 'firstPartyAncestor';
+    chrome.cookies.set(
+        firstPartyAncestor, pass(function(cookie) {
+          chrome.test.assertEq(
+              cookie.value,
+              firstPartyAncestor.value,
+          );
+          const expectedKey = {
+            topLevelSite: firstPartyAncestor.partitionKey.topLevelSite,
+            hasCrossSiteAncestor: false
+          };
+          chrome.test.assertEq(cookie.partitionKey, expectedKey);
+          chrome.cookies.remove({
+            name: firstPartyAncestor.name,
+            url: firstPartyAncestor.url,
+            partitionKey: expectedKey
+          });
+        }));
+
   },
   function getAllPartitionedCookies() {
     removeTestCookies();
diff --git a/chrome/test/data/extensions/platform_apps/web_view/shim/main.js b/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
index 135557b7e..25529e9 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/shim/main.js
@@ -3812,6 +3812,55 @@
   document.body.appendChild(webview);
 }
 
+// Before this test runs, the browser-side test code has a tab paired to a
+// Serial port for the same origin used in the webview. We confirm that the
+// webview cannot access or request the port.
+function testSerialDisabled() {
+  const webview = document.createElement('webview');
+  webview.src = embedder.emptyGuestURL;
+  webview.addEventListener('loadstop', async () => {
+    const getSerialPorts = async () => {
+      const ports = await navigator.serial.getPorts();
+      return ports;
+    };
+
+    const requestSerialPort = async () => {
+      const port = await navigator.serial.requestPort();
+      return port.getInfo;
+    };
+
+    try {
+      // Confirm that no port is available for WebView.
+      const result = await evalInWebView(webview, getSerialPorts, []);
+      embedder.test.assertEq(0, result.length);
+    } catch (_) {
+      embedder.test.fail();
+    }
+
+    try {
+      // Attempting to request a port should fail, expecting an exception.
+      await evalInWebView(webview, requestSerialPort, []);
+      // It's unexpected behavior for execution to end up here, so trigger a
+      // test failure.
+      embedder.test.fail();
+    } catch (_) {
+      // We expect an exception while requesting a port, so do nothing.
+    }
+
+    try {
+      // Confirm that there is still no port available.
+      const result = await evalInWebView(webview, getSerialPorts, []);
+      embedder.test.assertEq(0, result.length);
+    } catch (_) {
+      embedder.test.fail();
+    }
+
+    embedder.test.succeed();
+  });
+
+  document.body.appendChild(webview);
+}
+
 embedder.test.testList = {
   'testAllowTransparencyAttribute': testAllowTransparencyAttribute,
   'testAutosizeHeight': testAutosizeHeight,
@@ -3958,6 +4007,7 @@
   'testCannotRequestUsb': testCannotRequestUsb,
   'testCannotReuseUsbPairedInTab': testCannotReuseUsbPairedInTab,
   'testCannotRequestFonts': testCannotRequestFonts,
+  'testSerialDisabled': testSerialDisabled,
 };
 
 onload = function() {
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 6c1c8ea9..fd74ae8a 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -228,7 +228,6 @@
       "//chrome/browser/ash",
       "//chrome/browser/ash/crostini:test_support",
       "//chrome/browser/ash/extended_updates/test:test_support",
-      "//chrome/browser/ash/login/ui",
       "//chrome/browser/ash/ownership",
       "//chrome/browser/ash/system_web_apps/apps/personalization_app",
       "//chrome/browser/ash/system_web_apps/apps/personalization_app:test_support",
diff --git a/chrome/test/data/webui/downloads/item_test.ts b/chrome/test/data/webui/downloads/item_test.ts
index 05305e0..19aef728 100644
--- a/chrome/test/data/webui/downloads/item_test.ts
+++ b/chrome/test/data/webui/downloads/item_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import type {CrIconElement, CrToastManagerElement, ItemElement} from 'chrome://downloads/downloads.js';
+import type {CrIconElement, CrToastManagerElement, DownloadsItemElement} from 'chrome://downloads/downloads.js';
 import {BrowserProxy, DangerType, IconLoaderImpl, loadTimeData, SafeBrowsingState, State, TailoredWarningType} from 'chrome://downloads/downloads.js';
 import {stringToMojoString16, stringToMojoUrl} from 'chrome://resources/js/mojo_type_util.js';
 import {assertEquals, assertFalse, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
@@ -11,7 +11,7 @@
 import {createDownload, TestDownloadsProxy, TestIconLoader} from './test_support.js';
 
 suite('ItemTest', function() {
-  let item: ItemElement;
+  let item: DownloadsItemElement;
   let testDownloadsProxy: TestDownloadsProxy;
   let testIconLoader: TestIconLoader;
   let toastManager: CrToastManagerElement;
@@ -765,7 +765,7 @@
 });
 
 suite('ItemFocusTest', function() {
-  let item: ItemElement;
+  let item: DownloadsItemElement;
   let testDownloadsProxy: TestDownloadsProxy;
   let testIconLoader: TestIconLoader;
   let toastManager: CrToastManagerElement;
diff --git a/chrome/test/data/webui/lens/BUILD.gn b/chrome/test/data/webui/lens/BUILD.gn
index 19a80c3..53ba157 100644
--- a/chrome/test/data/webui/lens/BUILD.gn
+++ b/chrome/test/data/webui/lens/BUILD.gn
@@ -33,10 +33,14 @@
     "utils/text_utils.ts",
   ]
 
-  ts_path_mappings =
-      [ "chrome-untrusted://lens/*|" +
+  ts_path_mappings = [
+    "chrome-untrusted://lens-overlay/*|" +
         rebase_path("$root_gen_dir/chrome/browser/resources/lens/overlay/tsc/*",
-                    target_gen_dir) ]
+                    target_gen_dir),
+    "chrome-untrusted://lens/*|" +
+        rebase_path("$root_gen_dir/chrome/browser/resources/lens/overlay/tsc/*",
+                    target_gen_dir),
+  ]
 
   ts_definitions = [
     "//tools/typescript/definitions/chrome_event.d.ts",
diff --git a/chrome/test/data/webui/lens/lens_webui_browsertest.cc b/chrome/test/data/webui/lens/lens_webui_browsertest.cc
index 87a32a0..d7363bd3b 100644
--- a/chrome/test/data/webui/lens/lens_webui_browsertest.cc
+++ b/chrome/test/data/webui/lens/lens_webui_browsertest.cc
@@ -28,11 +28,12 @@
 
 using State = LensOverlayController::State;
 
+// TODO(crbug.com/365448173): Split overlay and side panel browser test into
+// different files.
 class LensWebUIBrowserTest : public WebUIMochaBrowserTest {
  protected:
   LensWebUIBrowserTest() {
     set_test_loader_scheme(content::kChromeUIUntrustedScheme);
-    set_test_loader_host(chrome::kChromeUILensHost);
   }
 
   void SetUp() override {
@@ -66,6 +67,7 @@
 class LensOverlayTest : public LensWebUIBrowserTest {
  protected:
   void RunOverlayTest(const std::string& file, const std::string& trigger) {
+    set_test_loader_host(chrome::kChromeUILensOverlayHost);
     WaitForPaint();
 
     // State should start in off.
@@ -92,6 +94,11 @@
     controller->ResetSearchboxHandler();
   }
 
+  void RunSidePanelTest(const std::string& file, const std::string& trigger) {
+    set_test_loader_host(chrome::kChromeUILensSidePanelHost);
+    RunTest(file, trigger);
+  }
+
   // Lens overlay takes a screenshot of the tab. In order to take a screenshot
   // the tab must not be about:blank and must be painted.
   void WaitForPaint() {
@@ -174,15 +181,15 @@
 
 using LensSidePanelTest = LensOverlayTest;
 IN_PROC_BROWSER_TEST_F(LensSidePanelTest, SidePanelResultsFrame) {
-  RunOverlayTest("lens/side_panel/results_frame_test.js", "mocha.run()");
+  RunSidePanelTest("lens/side_panel/results_frame_test.js", "mocha.run()");
 }
 
 IN_PROC_BROWSER_TEST_F(LensSidePanelTest, SearchboxBackButton) {
-  RunOverlayTest("lens/side_panel/searchbox_back_button_test.js",
-                 "mocha.run()");
+  RunSidePanelTest("lens/side_panel/searchbox_back_button_test.js",
+                   "mocha.run()");
 }
 
 IN_PROC_BROWSER_TEST_F(LensSidePanelTest, ErrorPage) {
-  RunOverlayTest("lens/side_panel/error_page_test.js", "mocha.run()");
+  RunSidePanelTest("lens/side_panel/error_page_test.js", "mocha.run()");
 }
 }  // namespace
diff --git a/chrome/test/data/webui/lens/overlay/cubic_bezier_test.ts b/chrome/test/data/webui/lens/overlay/cubic_bezier_test.ts
index 3cd09c96..d171fc9c 100644
--- a/chrome/test/data/webui/lens/overlay/cubic_bezier_test.ts
+++ b/chrome/test/data/webui/lens/overlay/cubic_bezier_test.ts
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/cubic_bezier.js';
+import 'chrome-untrusted://lens-overlay/cubic_bezier.js';
 
-import {CubicBezier} from 'chrome-untrusted://lens/cubic_bezier.js';
+import {CubicBezier} from 'chrome-untrusted://lens-overlay/cubic_bezier.js';
 import {assertEquals, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 
 // The tests for the cubic bezier easing calculations. Many of the tests in
diff --git a/chrome/test/data/webui/lens/overlay/find_words_in_region_test.ts b/chrome/test/data/webui/lens/overlay/find_words_in_region_test.ts
index 6d9c192..c6ede11 100644
--- a/chrome/test/data/webui/lens/overlay/find_words_in_region_test.ts
+++ b/chrome/test/data/webui/lens/overlay/find_words_in_region_test.ts
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/find_words_in_region.js';
+import 'chrome-untrusted://lens-overlay/find_words_in_region.js';
 
-import {areaOfPolygon, clip, ClippingEdge, findWordsInRegion, intersectionWithEdge, isInsideEdge, rotate, toPolygon} from 'chrome-untrusted://lens/find_words_in_region.js';
-import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
+import {areaOfPolygon, clip, ClippingEdge, findWordsInRegion, intersectionWithEdge, isInsideEdge, rotate, toPolygon} from 'chrome-untrusted://lens-overlay/find_words_in_region.js';
+import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 
 import {createWord} from '../utils/text_utils.js';
diff --git a/chrome/test/data/webui/lens/overlay/object_selection_test.ts b/chrome/test/data/webui/lens/overlay/object_selection_test.ts
index 6e608c48..ef7bd18 100644
--- a/chrome/test/data/webui/lens/overlay/object_selection_test.ts
+++ b/chrome/test/data/webui/lens/overlay/object_selection_test.ts
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/selection_overlay.js';
+import 'chrome-untrusted://lens-overlay/selection_overlay.js';
 
 import type {RectF} from '//resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {CenterRotatedBox} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import type {LensPageRemote} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import {UserAction} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import type {OverlayObject} from 'chrome-untrusted://lens/overlay_object.mojom-webui.js';
-import type {SelectionOverlayElement} from 'chrome-untrusted://lens/selection_overlay.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {CenterRotatedBox} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import type {LensPageRemote} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import {UserAction} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import type {OverlayObject} from 'chrome-untrusted://lens-overlay/overlay_object.mojom-webui.js';
+import type {SelectionOverlayElement} from 'chrome-untrusted://lens-overlay/selection_overlay.js';
 import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome-untrusted://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome-untrusted://webui-test/metrics_test_support.js';
diff --git a/chrome/test/data/webui/lens/overlay/overlay_background_scrim_test.ts b/chrome/test/data/webui/lens/overlay/overlay_background_scrim_test.ts
index 9deae00..34636be2 100644
--- a/chrome/test/data/webui/lens/overlay/overlay_background_scrim_test.ts
+++ b/chrome/test/data/webui/lens/overlay/overlay_background_scrim_test.ts
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/lens_overlay_app.js';
+import 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {LensOverlayAppElement} from 'chrome-untrusted://lens/lens_overlay_app.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {LensOverlayAppElement} from 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 import {waitBeforeNextRender} from 'chrome-untrusted://webui-test/polymer_test_util.js';
 
 import {TestLensOverlayBrowserProxy} from './test_overlay_browser_proxy.js';
diff --git a/chrome/test/data/webui/lens/overlay/overlay_close_button_test.ts b/chrome/test/data/webui/lens/overlay/overlay_close_button_test.ts
index 9b70be4..5b52c50 100644
--- a/chrome/test/data/webui/lens/overlay/overlay_close_button_test.ts
+++ b/chrome/test/data/webui/lens/overlay/overlay_close_button_test.ts
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/lens_overlay_app.js';
+import 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {LensOverlayAppElement} from 'chrome-untrusted://lens/lens_overlay_app.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {LensOverlayAppElement} from 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 import {waitBeforeNextRender} from 'chrome-untrusted://webui-test/polymer_test_util.js';
 
 import {TestLensOverlayBrowserProxy} from './test_overlay_browser_proxy.js';
diff --git a/chrome/test/data/webui/lens/overlay/overlay_cursor_test.ts b/chrome/test/data/webui/lens/overlay/overlay_cursor_test.ts
index d50f16d..703ed8c 100644
--- a/chrome/test/data/webui/lens/overlay/overlay_cursor_test.ts
+++ b/chrome/test/data/webui/lens/overlay/overlay_cursor_test.ts
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/lens_overlay_app.js';
+import 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 
 import type {RectF} from '//resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {CursorTooltipElement} from 'chrome-untrusted://lens/cursor_tooltip.js';
-import type {LensPageRemote} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import type {LensOverlayAppElement} from 'chrome-untrusted://lens/lens_overlay_app.js';
-import type {SelectionOverlayElement} from 'chrome-untrusted://lens/selection_overlay.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {CursorTooltipElement} from 'chrome-untrusted://lens-overlay/cursor_tooltip.js';
+import type {LensPageRemote} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import type {LensOverlayAppElement} from 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
+import type {SelectionOverlayElement} from 'chrome-untrusted://lens-overlay/selection_overlay.js';
 import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertStringContains, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome-untrusted://webui-test/polymer_test_util.js';
diff --git a/chrome/test/data/webui/lens/overlay/overlay_more_options_button_test.ts b/chrome/test/data/webui/lens/overlay/overlay_more_options_button_test.ts
index 497b0164..7dd6659 100644
--- a/chrome/test/data/webui/lens/overlay/overlay_more_options_button_test.ts
+++ b/chrome/test/data/webui/lens/overlay/overlay_more_options_button_test.ts
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/lens_overlay_app.js';
+import 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import {UserAction} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import type {LensOverlayAppElement} from 'chrome-untrusted://lens/lens_overlay_app.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import {UserAction} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import type {LensOverlayAppElement} from 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome-untrusted://webui-test/metrics_test_support.js';
 import {fakeMetricsPrivate} from 'chrome-untrusted://webui-test/metrics_test_support.js';
diff --git a/chrome/test/data/webui/lens/overlay/overlay_screenshot_test.ts b/chrome/test/data/webui/lens/overlay/overlay_screenshot_test.ts
index 88c5ffe..d52927ac 100644
--- a/chrome/test/data/webui/lens/overlay/overlay_screenshot_test.ts
+++ b/chrome/test/data/webui/lens/overlay/overlay_screenshot_test.ts
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/lens_overlay_app.js';
+import 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {LensOverlayAppElement} from 'chrome-untrusted://lens/lens_overlay_app.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {LensOverlayAppElement} from 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome-untrusted://webui-test/metrics_test_support.js';
 import {fakeMetricsPrivate} from 'chrome-untrusted://webui-test/metrics_test_support.js';
diff --git a/chrome/test/data/webui/lens/overlay/overlay_theme_test.ts b/chrome/test/data/webui/lens/overlay/overlay_theme_test.ts
index 1ae6dea..79398c48 100644
--- a/chrome/test/data/webui/lens/overlay/overlay_theme_test.ts
+++ b/chrome/test/data/webui/lens/overlay/overlay_theme_test.ts
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/lens_overlay_app.js';
+import 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {LensOverlayAppElement} from 'chrome-untrusted://lens/lens_overlay_app.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {LensOverlayAppElement} from 'chrome-untrusted://lens-overlay/lens_overlay_app.js';
 import {assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome-untrusted://webui-test/polymer_test_util.js';
 import {hasStyle} from 'chrome-untrusted://webui-test/test_util.js';
diff --git a/chrome/test/data/webui/lens/overlay/post_selection_renderer_test.ts b/chrome/test/data/webui/lens/overlay/post_selection_renderer_test.ts
index 37e5a7ab..602ef571 100644
--- a/chrome/test/data/webui/lens/overlay/post_selection_renderer_test.ts
+++ b/chrome/test/data/webui/lens/overlay/post_selection_renderer_test.ts
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/post_selection_renderer.js';
+import 'chrome-untrusted://lens-overlay/post_selection_renderer.js';
 
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {LensPageRemote} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import {UserAction} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import type {PostSelectionBoundingBox, PostSelectionRendererElement} from 'chrome-untrusted://lens/post_selection_renderer.js';
-import {CUTOUT_RADIUS_PX, MAX_CORNER_LENGTH_PX, MAX_CORNER_RADIUS_PX, MIN_BOX_SIZE_PX, PERIMETER_SELECTION_PADDING_PX} from 'chrome-untrusted://lens/post_selection_renderer.js';
-import type {GestureEvent} from 'chrome-untrusted://lens/selection_utils.js';
-import {GestureState} from 'chrome-untrusted://lens/selection_utils.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {LensPageRemote} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import {UserAction} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import type {PostSelectionBoundingBox, PostSelectionRendererElement} from 'chrome-untrusted://lens-overlay/post_selection_renderer.js';
+import {CUTOUT_RADIUS_PX, MAX_CORNER_LENGTH_PX, MAX_CORNER_RADIUS_PX, MIN_BOX_SIZE_PX, PERIMETER_SELECTION_PADDING_PX} from 'chrome-untrusted://lens-overlay/post_selection_renderer.js';
+import type {GestureEvent} from 'chrome-untrusted://lens-overlay/selection_utils.js';
+import {GestureState} from 'chrome-untrusted://lens-overlay/selection_utils.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome-untrusted://webui-test/metrics_test_support.js';
 import {fakeMetricsPrivate} from 'chrome-untrusted://webui-test/metrics_test_support.js';
diff --git a/chrome/test/data/webui/lens/overlay/region_selection_test.ts b/chrome/test/data/webui/lens/overlay/region_selection_test.ts
index 36385a4..612ce0a 100644
--- a/chrome/test/data/webui/lens/overlay/region_selection_test.ts
+++ b/chrome/test/data/webui/lens/overlay/region_selection_test.ts
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/selection_overlay.js';
+import 'chrome-untrusted://lens-overlay/selection_overlay.js';
 
 import type {Point, RectF} from '//resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import type {CenterRotatedBox} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import {UserAction} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import type {SelectionOverlayElement} from 'chrome-untrusted://lens/selection_overlay.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import type {CenterRotatedBox} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import {UserAction} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import type {SelectionOverlayElement} from 'chrome-untrusted://lens-overlay/selection_overlay.js';
 import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome-untrusted://webui-test/metrics_test_support.js';
diff --git a/chrome/test/data/webui/lens/overlay/selection_overlay_test.ts b/chrome/test/data/webui/lens/overlay/selection_overlay_test.ts
index d55535b..4abb9a2 100644
--- a/chrome/test/data/webui/lens/overlay/selection_overlay_test.ts
+++ b/chrome/test/data/webui/lens/overlay/selection_overlay_test.ts
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://lens/selection_overlay.js';
+import 'chrome-untrusted://lens-overlay/selection_overlay.js';
 
 import type {RectF} from '//resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import type {CenterRotatedBox} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import type {LensPageRemote} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import {UserAction} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import type {OverlayObject} from 'chrome-untrusted://lens/overlay_object.mojom-webui.js';
-import {ScreenshotBitmapBrowserProxyImpl} from 'chrome-untrusted://lens/screenshot_bitmap_browser_proxy.js';
-import type {SelectionOverlayElement} from 'chrome-untrusted://lens/selection_overlay.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import type {CenterRotatedBox} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import type {LensPageRemote} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import {UserAction} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import type {OverlayObject} from 'chrome-untrusted://lens-overlay/overlay_object.mojom-webui.js';
+import {ScreenshotBitmapBrowserProxyImpl} from 'chrome-untrusted://lens-overlay/screenshot_bitmap_browser_proxy.js';
+import type {SelectionOverlayElement} from 'chrome-untrusted://lens-overlay/selection_overlay.js';
 import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertStringContains, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome-untrusted://webui-test/metrics_test_support.js';
@@ -76,6 +76,12 @@
     };
   }
 
+  async function addEmptyText() {
+    const text = createText([]);
+    callbackRouterRemote.textReceived(text);
+    await waitAfterNextRender(selectionOverlayElement);
+  }
+
   async function addWords() {
     const text = createText([
       createParagraph([
@@ -90,7 +96,6 @@
       ]),
     ]);
     callbackRouterRemote.textReceived(text);
-    await flushTasks();
     await waitAfterNextRender(selectionOverlayElement);
   }
 
@@ -334,7 +339,7 @@
 
   // <if expr="not chromeos_lacros">
   test(
-      'verify that region search over text triggers detected text context menu',
+      'verify that region search over text triggers detected text options',
       async () => {
         await addWords();
 
@@ -347,8 +352,10 @@
         assertEquals(
             0,
             testBrowserProxy.handler.getCallCount('issueTextSelectionRequest'));
-        assertTrue(
-            selectionOverlayElement.getShowDetectedTextContextMenuForTesting());
+        assertTrue(selectionOverlayElement
+                       .getShowSelectedRegionContextMenuForTesting());
+        assertTrue(selectionOverlayElement
+                       .getShowDetectedTextContextMenuOptionsForTesting());
 
         testBrowserProxy.handler.reset();
         selectionOverlayElement.handleSelectTextForTesting();
@@ -361,7 +368,8 @@
       });
 
   test(
-      `verify that adding text after region selection triggers detected text context menu`,
+      `verify that adding text after region selection triggers detected text ` +
+          `options`,
       async () => {
         callbackRouterRemote.setPostRegionSelection({
           box: normalizedBox({x: 65, y: 25, width: 30, height: 30}),
@@ -376,8 +384,10 @@
         assertEquals(
             0,
             testBrowserProxy.handler.getCallCount('issueTextSelectionRequest'));
-        assertTrue(
-            selectionOverlayElement.getShowDetectedTextContextMenuForTesting());
+        assertTrue(selectionOverlayElement
+                       .getShowSelectedRegionContextMenuForTesting());
+        assertTrue(selectionOverlayElement
+                       .getShowDetectedTextContextMenuOptionsForTesting());
 
         testBrowserProxy.handler.reset();
         selectionOverlayElement.handleSelectTextForTesting();
@@ -389,41 +399,68 @@
             0, testBrowserProxy.handler.getCallCount('issueLensRegionRequest'));
       });
 
+  test('verify that select text in detected text options works', async () => {
+    await addWords();
+
+    await simulateDrag(selectionOverlayElement, {x: 0, y: 0}, {x: 80, y: 40});
+    selectionOverlayElement.handleSelectTextForTesting();
+
+    const textQuery =
+        await testBrowserProxy.handler.whenCalled('issueTextSelectionRequest');
+    assertDeepEquals('hello there test', textQuery);
+    assertEquals(
+        1, testBrowserProxy.handler.getCallCount('issueLensRegionRequest'));
+    assertFalse(
+        selectionOverlayElement.getShowSelectedRegionContextMenuForTesting());
+  });
+
+  test('verify that translate in detected text options works', async () => {
+    await addWords();
+
+    await simulateDrag(selectionOverlayElement, {x: 0, y: 0}, {x: 80, y: 40});
+    selectionOverlayElement.handleTranslateDetectedTextForTesting();
+
+    const textQuery = await testBrowserProxy.handler.whenCalled(
+        'issueTranslateSelectionRequest');
+    assertDeepEquals('hello there test', textQuery);
+    assertEquals(
+        1, testBrowserProxy.handler.getCallCount('issueLensRegionRequest'));
+    assertFalse(
+        selectionOverlayElement.getShowSelectedRegionContextMenuForTesting());
+    assertFalse(
+        selectionOverlayElement.getShowSelectedTextContextMenuForTesting());
+  });
+
   test(
-      'verify that select text in detected text context menu works',
+      'verify that copy as image in selected region context menu works',
       async () => {
         await addWords();
 
         await simulateDrag(
             selectionOverlayElement, {x: 0, y: 0}, {x: 80, y: 40});
-        selectionOverlayElement.handleSelectTextForTesting();
+        selectionOverlayElement.handleCopyAsImageForTesting();
 
-        const textQuery = await testBrowserProxy.handler.whenCalled(
-            'issueTextSelectionRequest');
-        assertDeepEquals('hello there test', textQuery);
-        assertEquals(
-            1, testBrowserProxy.handler.getCallCount('issueLensRegionRequest'));
-        assertFalse(
-            selectionOverlayElement.getShowDetectedTextContextMenuForTesting());
+        await testBrowserProxy.handler.whenCalled('copyImage');
+
+        // Verify context menu hides when an option is selected.
+        assertFalse(selectionOverlayElement
+                        .getShowSelectedRegionContextMenuForTesting());
       });
 
   test(
-      'verify that translate in detected text context menu works', async () => {
+      'verify that save as image in selected region context menu works',
+      async () => {
         await addWords();
 
         await simulateDrag(
             selectionOverlayElement, {x: 0, y: 0}, {x: 80, y: 40});
-        selectionOverlayElement.handleTranslateDetectedTextForTesting();
+        selectionOverlayElement.handleSaveAsImageForTesting();
 
-        const textQuery = await testBrowserProxy.handler.whenCalled(
-            'issueTranslateSelectionRequest');
-        assertDeepEquals('hello there test', textQuery);
-        assertEquals(
-            1, testBrowserProxy.handler.getCallCount('issueLensRegionRequest'));
-        assertFalse(
-            selectionOverlayElement.getShowDetectedTextContextMenuForTesting());
-        assertFalse(
-            selectionOverlayElement.getShowSelectedTextContextMenuForTesting());
+        await testBrowserProxy.handler.whenCalled('saveAsImage');
+
+        // Verify context menu hides when an option is selected.
+        assertFalse(selectionOverlayElement
+                        .getShowSelectedRegionContextMenuForTesting());
       });
   // </if>
 
@@ -454,6 +491,21 @@
         expectedLeft.toString().substring(0, 6));
   });
 
+  test(
+      'verify that region search triggers selected region context menu',
+      async () => {
+        await addEmptyText();
+
+        await simulateDrag(
+            selectionOverlayElement, {x: 50, y: 25}, {x: 300, y: 200});
+        await waitAfterNextRender(selectionOverlayElement);
+
+        assertTrue(selectionOverlayElement
+                       .getShowSelectedRegionContextMenuForTesting());
+        assertFalse(selectionOverlayElement
+                        .getShowDetectedTextContextMenuOptionsForTesting());
+      });
+
   test('verify that tapping an object triggers post selection', async () => {
     await addObjects();
     const objectEl = selectionOverlayElement.$.objectSelectionLayer
@@ -592,7 +644,7 @@
         });
 
     assertFalse(
-        selectionOverlayElement.getShowDetectedTextContextMenuForTesting());
+        selectionOverlayElement.getShowSelectedRegionContextMenuForTesting());
     assertTrue(
         selectionOverlayElement.getShowSelectedTextContextMenuForTesting());
 
@@ -636,12 +688,11 @@
               y: wordElBoundingBox.bottom,
             });
 
-        assertFalse(
-            selectionOverlayElement.getShowDetectedTextContextMenuForTesting());
+        assertFalse(selectionOverlayElement
+                        .getShowSelectedRegionContextMenuForTesting());
         assertTrue(
             selectionOverlayElement.getShowSelectedTextContextMenuForTesting());
 
-
         selectionOverlayElement.handleTranslateForTesting();
         const textQuery = await testBrowserProxy.handler.whenCalled(
             'issueTranslateSelectionRequest');
@@ -1074,8 +1125,8 @@
         assertDeepEquals('wow a translation no', textQuery);
         assertEquals(
             0, testBrowserProxy.handler.getCallCount('issueLensRegionRequest'));
-        assertFalse(
-            selectionOverlayElement.getShowDetectedTextContextMenuForTesting());
+        assertFalse(selectionOverlayElement
+                        .getShowSelectedRegionContextMenuForTesting());
         assertEquals(
             1,
             metrics.count(
@@ -1111,8 +1162,8 @@
             {x: 80, y: 40});
 
         await waitAfterNextRender(selectionOverlayElement);
-        assertFalse(
-            selectionOverlayElement.getShowDetectedTextContextMenuForTesting());
+        assertFalse(selectionOverlayElement
+                        .getShowSelectedRegionContextMenuForTesting());
         assertTrue(
             selectionOverlayElement.getShowSelectedTextContextMenuForTesting());
 
diff --git a/chrome/test/data/webui/lens/overlay/test_language_browser_proxy.ts b/chrome/test/data/webui/lens/overlay/test_language_browser_proxy.ts
index 1710884..d1876f7b 100644
--- a/chrome/test/data/webui/lens/overlay/test_language_browser_proxy.ts
+++ b/chrome/test/data/webui/lens/overlay/test_language_browser_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 {LanguageBrowserProxy} from 'chrome-untrusted://lens/language_browser_proxy.js';
+import type {LanguageBrowserProxy} from 'chrome-untrusted://lens-overlay/language_browser_proxy.js';
 
 /**
  * Test version of the LanguageBrowserProxy used in connecting Lens Overlay to
diff --git a/chrome/test/data/webui/lens/overlay/test_overlay_browser_proxy.ts b/chrome/test/data/webui/lens/overlay/test_overlay_browser_proxy.ts
index 4ad7ead..e01336f 100644
--- a/chrome/test/data/webui/lens/overlay/test_overlay_browser_proxy.ts
+++ b/chrome/test/data/webui/lens/overlay/test_overlay_browser_proxy.ts
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import type {BrowserProxy} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {CenterRotatedBox} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import type {LensPageHandlerInterface, LensPageRemote, UserAction} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import {LensPageCallbackRouter} from 'chrome-untrusted://lens/lens.mojom-webui.js';
+import type {BrowserProxy} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {CenterRotatedBox} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import type {LensPageHandlerInterface, LensPageRemote, UserAction} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import {LensPageCallbackRouter} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
 import type {ClickModifiers} from 'chrome-untrusted://resources/mojo/ui/base/mojom/window_open_disposition.mojom-webui.js';
 import {TestBrowserProxy} from 'chrome-untrusted://webui-test/test_browser_proxy.js';
 
@@ -33,6 +33,8 @@
       'issueTranslateFullPageRequest',
       'notifyOverlayInitialized',
       'copyText',
+      'copyImage',
+      'saveAsImage',
       'recordUkmAndTaskCompletionForLensOverlayInteraction',
     ]);
   }
diff --git a/chrome/test/data/webui/lens/overlay/text_selection_test.ts b/chrome/test/data/webui/lens/overlay/text_selection_test.ts
index 20d32d5..d9811fd2 100644
--- a/chrome/test/data/webui/lens/overlay/text_selection_test.ts
+++ b/chrome/test/data/webui/lens/overlay/text_selection_test.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 'chrome-untrusted://lens/selection_overlay.js';
+import 'chrome-untrusted://lens-overlay/selection_overlay.js';
 
 import type {RectF} from '//resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import type {LensPageRemote} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import {UserAction} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import type {SelectionOverlayElement} from 'chrome-untrusted://lens/selection_overlay.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import type {LensPageRemote} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import {UserAction} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import type {SelectionOverlayElement} from 'chrome-untrusted://lens-overlay/selection_overlay.js';
 import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js';
 import {assertEquals} from 'chrome-untrusted://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome-untrusted://webui-test/metrics_test_support.js';
diff --git a/chrome/test/data/webui/lens/overlay/translate_button_test.ts b/chrome/test/data/webui/lens/overlay/translate_button_test.ts
index 8e69076e..0febea1 100644
--- a/chrome/test/data/webui/lens/overlay/translate_button_test.ts
+++ b/chrome/test/data/webui/lens/overlay/translate_button_test.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 'chrome-untrusted://lens/translate_button.js';
+import 'chrome-untrusted://lens-overlay/translate_button.js';
 
-import {BrowserProxyImpl} from 'chrome-untrusted://lens/browser_proxy.js';
-import {LanguageBrowserProxyImpl} from 'chrome-untrusted://lens/language_browser_proxy.js';
-import {UserAction} from 'chrome-untrusted://lens/lens.mojom-webui.js';
-import {ShimmerControlRequester} from 'chrome-untrusted://lens/selection_utils.js';
-import type {TranslateButtonElement} from 'chrome-untrusted://lens/translate_button.js';
+import {BrowserProxyImpl} from 'chrome-untrusted://lens-overlay/browser_proxy.js';
+import {LanguageBrowserProxyImpl} from 'chrome-untrusted://lens-overlay/language_browser_proxy.js';
+import {UserAction} from 'chrome-untrusted://lens-overlay/lens.mojom-webui.js';
+import {ShimmerControlRequester} from 'chrome-untrusted://lens-overlay/selection_utils.js';
+import type {TranslateButtonElement} from 'chrome-untrusted://lens-overlay/translate_button.js';
 import type {CrButtonElement} from 'chrome-untrusted://resources/cr_elements/cr_button/cr_button.js';
 import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
diff --git a/chrome/test/data/webui/lens/utils/object_utils.ts b/chrome/test/data/webui/lens/utils/object_utils.ts
index 5313065..1fa76d8 100644
--- a/chrome/test/data/webui/lens/utils/object_utils.ts
+++ b/chrome/test/data/webui/lens/utils/object_utils.ts
@@ -3,11 +3,11 @@
 // found in the LICENSE file.
 
 import type {RectF} from '//resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import type {CenterRotatedBox} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import type {OverlayObject} from 'chrome-untrusted://lens/overlay_object.mojom-webui.js';
-import type {Polygon} from 'chrome-untrusted://lens/polygon.mojom-webui.js';
-import {Polygon_CoordinateType, Polygon_VertexOrdering} from 'chrome-untrusted://lens/polygon.mojom-webui.js';
+import type {CenterRotatedBox} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import type {OverlayObject} from 'chrome-untrusted://lens-overlay/overlay_object.mojom-webui.js';
+import type {Polygon} from 'chrome-untrusted://lens-overlay/polygon.mojom-webui.js';
+import {Polygon_CoordinateType, Polygon_VertexOrdering} from 'chrome-untrusted://lens-overlay/polygon.mojom-webui.js';
 import {assertEquals, assertLT, assertNotEquals} from 'chrome-untrusted://webui-test/chai_assert.js';
 
 export function assertWithinThreshold(value1: number, value2: number): void {
diff --git a/chrome/test/data/webui/lens/utils/selection_utils.ts b/chrome/test/data/webui/lens/utils/selection_utils.ts
index c825c87..3d4062f9 100644
--- a/chrome/test/data/webui/lens/utils/selection_utils.ts
+++ b/chrome/test/data/webui/lens/utils/selection_utils.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import type {Point} from '//resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import type {SelectionOverlayElement} from 'chrome-untrusted://lens/selection_overlay.js';
+import type {SelectionOverlayElement} from 'chrome-untrusted://lens-overlay/selection_overlay.js';
 import {flushTasks, waitAfterNextRender} from 'chrome-untrusted://webui-test/polymer_test_util.js';
 
 function createPointerEvent(
diff --git a/chrome/test/data/webui/lens/utils/text_utils.ts b/chrome/test/data/webui/lens/utils/text_utils.ts
index a2a6115..88ba5262 100644
--- a/chrome/test/data/webui/lens/utils/text_utils.ts
+++ b/chrome/test/data/webui/lens/utils/text_utils.ts
@@ -4,9 +4,9 @@
 
 import {hexColorToSkColor} from '//resources/js/color_utils.js';
 import type {RectF} from '//resources/mojo/ui/gfx/geometry/mojom/geometry.mojom-webui.js';
-import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens/geometry.mojom-webui.js';
-import type {Line, Paragraph, Text, TranslatedLine, TranslatedParagraph, Word} from 'chrome-untrusted://lens/text.mojom-webui.js';
-import {Alignment, WritingDirection} from 'chrome-untrusted://lens/text.mojom-webui.js';
+import {CenterRotatedBox_CoordinateType} from 'chrome-untrusted://lens-overlay/geometry.mojom-webui.js';
+import type {Line, Paragraph, Text, TranslatedLine, TranslatedParagraph, Word} from 'chrome-untrusted://lens-overlay/text.mojom-webui.js';
+import {Alignment, WritingDirection} from 'chrome-untrusted://lens-overlay/text.mojom-webui.js';
 
 export function createText(paragraphs: Paragraph[]): Text {
   return {
diff --git a/chrome/test/data/webui/side_panel/read_anything/BUILD.gn b/chrome/test/data/webui/side_panel/read_anything/BUILD.gn
index 072a0e6..b4e76cc 100644
--- a/chrome/test/data/webui/side_panel/read_anything/BUILD.gn
+++ b/chrome/test/data/webui/side_panel/read_anything/BUILD.gn
@@ -53,6 +53,7 @@
     "speech_uses_max_text_length_test.ts",
     "word_boundaries_test.ts",
     "word_highlighting_test.ts",
+    "phrase_highlighting_test.ts",
     "word_highlighting_disabled_test.ts",
     "update_voice_pack_test.ts",
     "toolbar_test.ts",
diff --git a/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts b/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts
new file mode 100644
index 0000000..569c8dd
--- /dev/null
+++ b/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts
@@ -0,0 +1,237 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import type {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import {flush} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {BrowserProxy} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
+import type {AppElement, ReadAnythingToolbarElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
+import {assertEquals, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
+
+import {stubAnimationFrame, suppressInnocuousErrors} from './common.js';
+import {TestColorUpdaterBrowserProxy} from './test_color_updater_browser_proxy.js';
+
+suite('PhraseHighlighting', () => {
+  let app: AppElement;
+  let testBrowserProxy: TestColorUpdaterBrowserProxy;
+
+  // root htmlTag='#document' id=1
+  // ++link htmlTag='a' url='http://www.google.com' id=2
+  // ++++staticText name='This is a link.' id=3
+  // ++link htmlTag='a' url='http://www.youtube.com' id=4
+  // ++++staticText name='This is another link.' id=5
+  const axTree = {
+    rootId: 1,
+    nodes: [
+      {
+        id: 1,
+        role: 'rootWebArea',
+        htmlTag: '#document',
+        childIds: [2, 4],
+      },
+      {
+        id: 2,
+        role: 'link',
+        htmlTag: 'a',
+        url: 'http://www.google.com',
+        childIds: [3],
+      },
+      {
+        id: 3,
+        role: 'staticText',
+        name: 'This is a link.',
+      },
+      {
+        id: 4,
+        role: 'link',
+        htmlTag: 'a',
+        url: 'http://www.youtube.com',
+        childIds: [5],
+      },
+      {
+        id: 5,
+        role: 'staticText',
+        name: 'This is another link.',
+      },
+    ],
+  };
+
+  setup(() => {
+    suppressInnocuousErrors();
+    testBrowserProxy = new TestColorUpdaterBrowserProxy();
+    BrowserProxy.setInstance(testBrowserProxy);
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    // Do not call the real `onConnected()`. As defined in
+    // ReadAnythingAppController, onConnected creates mojo pipes to connect to
+    // the rest of the Read Anything feature, which we are not testing here.
+    chrome.readingMode.onConnected = () => {};
+
+    app = document.createElement('read-anything-app');
+    document.body.appendChild(app);
+    flush();
+
+    // Use a tree with just one sentence. For the actual implementation of
+    // phrase segmentation, a more realistic example would be to use
+    // setSimpleAxTreeWithText instead.
+    chrome.readingMode.setContentForTesting(axTree, [2, 4]);
+  });
+
+  function computeStyle(style: string) {
+    return window.getComputedStyle(app.$.container).getPropertyValue(style);
+  }
+
+  suite('changing the highlight from the menu', () => {
+    let toolbar: ReadAnythingToolbarElement;
+    let highlightButton: CrIconButtonElement;
+    let options: HTMLButtonElement[];
+
+    setup(() => {
+      toolbar = app.$.toolbar;
+      highlightButton =
+          toolbar.$.toolbarContainer.querySelector<CrIconButtonElement>(
+              '#highlight')!;
+      stubAnimationFrame();
+      highlightButton.click();
+      flush();
+
+      const menu = toolbar.$.highlightMenu.$.menu.$.lazyMenu.get();
+      assertTrue(menu.open);
+      options = Array.from(
+          menu.querySelectorAll<HTMLButtonElement>('.dropdown-item'));
+    });
+
+    test('with word highlighting on, word is highlighted', () => {
+      options[1]!.click();
+      flush();
+      assertEquals(
+          chrome.readingMode.highlightGranularity,
+          chrome.readingMode.wordHighlighting);
+
+      app.updateBoundary(0);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals(currentHighlight!.textContent!, 'This ');
+    });
+
+    test('with phrase highlighting on, phrase is highlighted', () => {
+      options[2]!.click();
+      flush();
+      assertEquals(
+          chrome.readingMode.highlightGranularity,
+          chrome.readingMode.phraseHighlighting);
+
+      app.updateBoundary(0);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals(currentHighlight!.textContent!, 'This is a ');
+    });
+
+    test('with sentence highlighting on, sentence is highlighted', () => {
+      options[3]!.click();
+      flush();
+      assertEquals(
+          chrome.readingMode.highlightGranularity,
+          chrome.readingMode.sentenceHighlighting);
+
+      app.updateBoundary(0);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals(currentHighlight!.textContent!, 'This is a link.');
+    });
+
+    test('with highlighting off, highlight is invisible', () => {
+      options[4]!.click();
+      flush();
+      assertEquals(
+          chrome.readingMode.highlightGranularity,
+          chrome.readingMode.noHighlighting);
+
+      app.updateBoundary(0);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals('transparent', computeStyle('--current-highlight-bg-color'));
+    });
+  });
+
+  suite('after a word boundary', () => {
+    setup(() => {
+      app.updateBoundary(0);
+    });
+
+    test('initially, phrase is highlighted', () => {
+      chrome.readingMode.onHighlightGranularityChanged(
+          chrome.readingMode.phraseHighlighting);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals(currentHighlight!.textContent!, 'This is a ');
+    });
+
+    test('phrase highlight same after second word boundary', () => {
+      chrome.readingMode.onHighlightGranularityChanged(
+          chrome.readingMode.phraseHighlighting);
+      app.updateBoundary(5);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals(currentHighlight!.textContent!, 'This is a ');
+    });
+
+    test('phrase highlighting highlights second phrase', () => {
+      chrome.readingMode.onHighlightGranularityChanged(
+          chrome.readingMode.phraseHighlighting);
+      app.updateBoundary(10);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals(currentHighlight!.textContent!, 'link.');
+    });
+
+    // Tests for checking correct handling of auto granularity.
+    test(
+        'with auto highlighting and rate of 2, sentence highlight used', () => {
+          chrome.readingMode.onHighlightGranularityChanged(
+              chrome.readingMode.sentenceHighlighting);
+          app.playSpeech();
+          const currentHighlight =
+              app.$.container.querySelector('.current-read-highlight');
+          assertTrue(currentHighlight !== undefined);
+          assertEquals('This is a link.', currentHighlight!.textContent);
+        });
+
+    test('with auto highlighting and rate of 1, phrase highlight used', () => {
+      chrome.readingMode.onHighlightGranularityChanged(
+          chrome.readingMode.autoHighlighting);
+      chrome.readingMode.onSpeechRateChange(1);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals('This is a ', currentHighlight!.textContent);
+    });
+
+    test('with auto highlighting and rate of 0.5, word highlight used', () => {
+      chrome.readingMode.onHighlightGranularityChanged(
+          chrome.readingMode.autoHighlighting);
+      chrome.readingMode.onSpeechRateChange(0.5);
+      app.playSpeech();
+      const currentHighlight =
+          app.$.container.querySelector('.current-read-highlight');
+      assertTrue(currentHighlight !== undefined);
+      assertEquals('This ', currentHighlight!.textContent);
+    });
+
+    // TODO(b/364327601): Add tests for unsupported language handling.
+  });
+});
diff --git a/chrome/test/data/webui/side_panel/read_anything/read_anything_browsertest.cc b/chrome/test/data/webui/side_panel/read_anything/read_anything_browsertest.cc
index 450094850..298cc6b1 100644
--- a/chrome/test/data/webui/side_panel/read_anything/read_anything_browsertest.cc
+++ b/chrome/test/data/webui/side_panel/read_anything/read_anything_browsertest.cc
@@ -266,7 +266,7 @@
     scoped_feature_list_.InitWithFeatures(
         {features::kReadAnythingReadAloud,
          features::kReadAnythingReadAloudAutomaticWordHighlighting},
-        {});
+        {features::kReadAnythingReadAloudPhraseHighlighting});
   }
 
  private:
@@ -329,3 +329,9 @@
   RunSidePanelTest("side_panel/read_anything/highlight_menu_test.js",
                    "mocha.run()");
 }
+
+IN_PROC_BROWSER_TEST_F(ReadAnythingReadAloudPhraseHighlightingMochaTest,
+                       PhraseHighlighting) {
+  RunSidePanelTest("side_panel/read_anything/phrase_highlighting_test.js",
+                   "mocha.run()");
+}
diff --git a/chrome/test/data/webui/signin/legacy_managed_user_profile_notice_test.ts b/chrome/test/data/webui/signin/legacy_managed_user_profile_notice_test.ts
index 9c7b7f3..9efe6e1e 100644
--- a/chrome/test/data/webui/signin/legacy_managed_user_profile_notice_test.ts
+++ b/chrome/test/data/webui/signin/legacy_managed_user_profile_notice_test.ts
@@ -37,7 +37,9 @@
       subtitle: 'subtitle',
       enterpriseInfo: 'enterprise_info',
       proceedLabel: 'proceed_label',
-      showCancelButton: true,
+      accountName: 'account_name',
+      continueAs: 'continue_as',
+      email: 'email@email.com',
       checkLinkDataCheckboxByDefault: false,
     };
 
@@ -63,10 +65,7 @@
     /**
      * Checks that the expected image url is displayed.
      */
-    function checkImageUrl(expectedUrl: string) {
-      const targetElement = useUpdatedUi ?
-          app.shadowRoot!.querySelector<HTMLElement>('#disclosure')! :
-          app;
+    function checkImageUrl(targetElement: HTMLElement, expectedUrl: string) {
       assertTrue(isChildVisible(targetElement, '#avatar'));
       const img =
           targetElement.shadowRoot!.querySelector<HTMLImageElement>('#avatar')!;
@@ -151,7 +150,9 @@
         subtitle: 'subtitle',
         enterpriseInfo: 'enterprise_info',
         proceedLabel: 'proceed_label',
-        showCancelButton: true,
+        accountName: 'account_name',
+        continueAs: 'continue_as',
+        email: 'email@email.com',
         checkLinkDataCheckboxByDefault: true,
       });
       await microtasksFinished();
@@ -184,6 +185,37 @@
       const proceedButton =
           app.shadowRoot!.querySelector<HTMLElement>('#proceed-button')!;
 
+      webUIListenerCallback('on-state-changed', State.VALUE_PROPOSITION);
+      await microtasksFinished();
+      assertTrue(
+          isChildVisible(app, '#value-prop'),
+          'Value proposition State: #value-prop');
+      assertFalse(
+          isChildVisible(app, '#disclosure'),
+          'Value proposition State: #disclosure');
+      assertFalse(
+          isChildVisible(app, '#processing'),
+          'Value proposition State: #processing');
+      assertFalse(
+          isChildVisible(app, '#error'), 'Value proposition State: #error');
+      assertFalse(
+          isChildVisible(app, '#timeout'), 'Value proposition State: #timeout');
+      assertFalse(
+          isChildVisible(app, '#success'), 'Value proposition State: #success');
+      assertTrue(
+          isChildVisible(app, '#proceed-button'),
+          'Value proposition State: #proceed-button');
+      assertTrue(
+          isChildVisible(app, '#cancel-button'),
+          'Value proposition State: #cancel-button');
+      assertEquals(
+          'continue_as', proceedButton.textContent!.trim(),
+          'Value proposition State: Proceed label');
+
+      webUIListenerCallback('on-state-changed', State.DISCLOSURE);
+      await microtasksFinished();
+      assertFalse(
+          isChildVisible(app, '#value-prop'), 'Disclosure State: #value-prop');
       assertTrue(
           isChildVisible(app, '#disclosure'), 'Disclosure State: #disclosure');
       assertFalse(
@@ -206,6 +238,8 @@
       webUIListenerCallback('on-state-changed', State.PROCESSING);
       await microtasksFinished();
       assertFalse(
+          isChildVisible(app, '#value-prop'), 'Processing State: #value-prop');
+      assertFalse(
           isChildVisible(app, '#disclosure'), 'Processing State: #disclosure');
       assertTrue(
           isChildVisible(app, '#processing'), 'Processing State: #processing');
@@ -224,6 +258,8 @@
       webUIListenerCallback('on-state-changed', State.ERROR);
       await microtasksFinished();
       assertFalse(
+          isChildVisible(app, '#value-prop'), 'Error State: #value-prop');
+      assertFalse(
           isChildVisible(app, '#disclosure'), 'Error State: #disclosure');
       assertFalse(
           isChildVisible(app, '#processing'), 'Error State: #processing');
@@ -242,6 +278,8 @@
       webUIListenerCallback('on-state-changed', State.TIMEOUT);
       await microtasksFinished();
       assertFalse(
+          isChildVisible(app, '#value-prop'), 'Timeout State: #value-prop');
+      assertFalse(
           isChildVisible(app, '#disclosure'), 'Timeout State: #disclosure');
       assertFalse(
           isChildVisible(app, '#processing'), 'Timeout State: #processing');
@@ -261,6 +299,8 @@
       webUIListenerCallback('on-state-changed', State.SUCCESS);
       await microtasksFinished();
       assertFalse(
+          isChildVisible(app, '#value-prop'), 'Success State: #value-prop');
+      assertFalse(
           isChildVisible(app, '#disclosure'), 'Success State: #disclosure');
       assertFalse(
           isChildVisible(app, '#processing'), 'Success State: #processing');
@@ -278,7 +318,11 @@
           'Success State: Proceed label');
     });
 
-    test('onProfileInfoChanged', async function() {
+    test('onProfileInfoChangedDisclosureSection', async function() {
+      // Navigate to the disclosure section.
+      webUIListenerCallback('on-state-changed', State.DISCLOSURE);
+      await microtasksFinished();
+
       const targetElement = useUpdatedUi ?
           app.shadowRoot!.querySelector<HTMLElement>('#disclosure')! :
           app;
@@ -316,7 +360,7 @@
           useUpdatedUi ? app.i18n('profileDisclosureSubtitle') : 'subtitle',
           'enterprise_info',
           useUpdatedUi ? app.i18n('continueLabel') : 'proceed_label');
-      checkImageUrl(AVATAR_URL_1);
+      checkImageUrl(targetElement, AVATAR_URL_1);
       assertFalse(isChildVisible(targetElement, '.work-badge'));
 
       // Update the values.
@@ -327,7 +371,9 @@
         subtitle: 'new_subtitle',
         enterpriseInfo: 'new_enterprise_info',
         proceedLabel: 'new_proceed_label',
-        showCancelButton: false,
+        accountName: 'new_account_name',
+        continueAs: 'new_continue_as',
+        email: 'new_email@email.com',
         checkLinkDataCheckboxByDefault: false,
       });
       await microtasksFinished();
@@ -337,9 +383,76 @@
           useUpdatedUi ? app.i18n('profileDisclosureSubtitle') : 'new_subtitle',
           'new_enterprise_info',
           useUpdatedUi ? app.i18n('continueLabel') : 'new_proceed_label');
-      checkImageUrl(AVATAR_URL_2);
+      checkImageUrl(targetElement, AVATAR_URL_2);
       assertTrue(isChildVisible(targetElement, '.work-badge'));
-      assertFalse(isChildVisible(app, '#cancel-button'));
+      assertTrue(isChildVisible(app, '#cancel-button'));
+    });
+
+    test('onProfileInfoChangedValuePropositionSection', async function() {
+      if (!useUpdatedUi) {
+        return;
+      }
+      webUIListenerCallback('on-state-changed', State.VALUE_PROPOSITION);
+      await microtasksFinished();
+
+      const targetElement =
+          app.shadowRoot!.querySelector<HTMLElement>('#value-prop')!;
+      // Helper to test all the text values in the UI in the disclosure screenl.
+      function checkValuePropositionTextValues(
+          expectedTitle: string, expectedSubtitle: string,
+          expectedEmail: string, expectedAccountName: string,
+          expectedProceedLabel: string) {
+        const target =
+            app.shadowRoot!.querySelector<HTMLElement>('#value-prop')!;
+        assertTrue(isChildVisible(target, '.title'));
+        const titleElement =
+            target.shadowRoot!.querySelector<HTMLElement>('.title')!;
+        assertEquals(expectedTitle, titleElement.textContent!.trim());
+        assertTrue(isChildVisible(target, '.subtitle'));
+        const subtitleElement =
+            target.shadowRoot!.querySelector<HTMLElement>('.subtitle')!;
+        assertEquals(expectedSubtitle, subtitleElement.textContent!.trim());
+        assertTrue(isChildVisible(target, '.email'));
+        const emailElement =
+            target.shadowRoot!.querySelector<HTMLElement>('.email')!;
+        assertEquals(expectedEmail, emailElement.textContent!.trim());
+        assertTrue(isChildVisible(target, '.account-name'));
+        const accountNameElement =
+            target.shadowRoot!.querySelector<HTMLElement>('.account-name')!;
+        assertEquals(
+            expectedAccountName, accountNameElement.textContent!.trim());
+        assertTrue(isChildVisible(app, '#proceed-button'));
+        const proceedButton =
+            app.shadowRoot!.querySelector<HTMLElement>('#proceed-button')!;
+        assertEquals(expectedProceedLabel, proceedButton.textContent!.trim());
+      }
+
+      // Initial values.
+      checkValuePropositionTextValues(
+          app.i18n('signinIntoChrome'), app.i18n('valuePropSubtitle'),
+          'email@email.com', 'account_name', 'continue_as');
+      checkImageUrl(targetElement, AVATAR_URL_1);
+
+      // Update the values.
+      webUIListenerCallback('on-profile-info-changed', {
+        pictureUrl: AVATAR_URL_2,
+        showEnterpriseBadge: true,
+        title: 'new_title',
+        subtitle: 'new_subtitle',
+        enterpriseInfo: 'new_enterprise_info',
+        proceedLabel: 'new_proceed_label',
+        accountName: 'new_account_name',
+        continueAs: 'new_continue_as',
+        email: 'new_email@email.com',
+        checkLinkDataCheckboxByDefault: false,
+      });
+      await microtasksFinished();
+      checkValuePropositionTextValues(
+          app.i18n('signinIntoChrome'), app.i18n('valuePropSubtitle'),
+          'new_email@email.com', 'new_account_name', 'new_continue_as');
+
+      checkImageUrl(targetElement, AVATAR_URL_2);
+      assertTrue(isChildVisible(app, '#cancel-button'));
     });
   });
 });
diff --git a/chrome/test/data/webui/tab_search/auto_tab_groups_page_test.ts b/chrome/test/data/webui/tab_search/auto_tab_groups_page_test.ts
index 9f31d99..ebe207e 100644
--- a/chrome/test/data/webui/tab_search/auto_tab_groups_page_test.ts
+++ b/chrome/test/data/webui/tab_search/auto_tab_groups_page_test.ts
@@ -7,7 +7,7 @@
 import type {AutoTabGroupsPageElement, AutoTabGroupsResultsElement, CrInputElement, TabOrganizationSession} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {TabOrganizationError, TabOrganizationState, TabSearchApiProxyImpl, TabSearchSyncBrowserProxyImpl} from 'chrome://tab-search.top-chrome/tab_search.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
+import {isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {createProfileData, createTab} from './tab_search_test_data.js';
 import {TestTabSearchApiProxy} from './test_tab_search_api_proxy.js';
@@ -447,10 +447,11 @@
     const successString = 'success';
     loadTimeData.overrideValues({
       successMissingActiveTabTitle: errorString,
-      successTitle: successString,
+      successTitleSingle: successString,
     });
-    await autoTabGroupsResultsSetup();
-    autoTabGroupsResults.session = createSession({
+    await autoTabGroupsPageSetup();
+    const session = createSession({
+      state: TabOrganizationState.kSuccess,
       activeTabId: 4,
       organizations: [{
         organizationId: 1,
@@ -466,9 +467,12 @@
         ],
       }],
     });
+    testApiProxy.getCallbackRouterRemote().tabOrganizationSessionUpdated(
+        session);
     await microtasksFinished();
 
-    const header = autoTabGroupsResults.$.header;
+    const header = autoTabGroupsPage.shadowRoot!.querySelector('#header');
+    assertTrue(!!header);
     assertEquals(errorString, header.textContent!.trim());
   });
 
@@ -477,10 +481,11 @@
     const successString = 'success';
     loadTimeData.overrideValues({
       successMissingActiveTabTitle: errorString,
-      successTitle: successString,
+      successTitleSingle: successString,
     });
-    await autoTabGroupsResultsSetup();
-    autoTabGroupsResults.session = createSession({
+    await autoTabGroupsPageSetup();
+    const session = createSession({
+      state: TabOrganizationState.kSuccess,
       activeTabId: 2,
       organizations: [{
         organizationId: 1,
@@ -496,77 +501,12 @@
         ],
       }],
     });
+    testApiProxy.getCallbackRouterRemote().tabOrganizationSessionUpdated(
+        session);
     await microtasksFinished();
 
-    const header = autoTabGroupsResults.$.header;
+    const header = autoTabGroupsPage.shadowRoot!.querySelector('#header');
+    assertTrue(!!header);
     assertEquals(successString, header.textContent!.trim());
   });
-
-  test('Announces not started header on state change', async () => {
-    const notStartedHeader = 'Not Started';
-    loadTimeData.overrideValues({
-      notStartedTitleFRE: notStartedHeader,
-    });
-    const announcementPromise =
-        eventToPromise('cr-a11y-announcer-messages-sent', document.body);
-    await autoTabGroupsPageSetup();
-
-    const announcement = await announcementPromise;
-    assertTrue(!!announcement);
-    assertTrue(announcement.detail.messages.includes(notStartedHeader));
-  });
-
-  test('Announces in progress header on state change', async () => {
-    const inProgressHeader = 'In Progress';
-    loadTimeData.overrideValues({
-      inProgressTitle: inProgressHeader,
-    });
-    await autoTabGroupsPageSetup();
-
-    const announcementPromise =
-        eventToPromise('cr-a11y-announcer-messages-sent', document.body);
-    testApiProxy.getCallbackRouterRemote().tabOrganizationSessionUpdated(
-        createSession({state: TabOrganizationState.kInProgress}));
-
-    const announcement = await announcementPromise;
-    assertTrue(!!announcement);
-    assertTrue(announcement.detail.messages.includes(inProgressHeader));
-  });
-
-  test('Announces results header on state change', async () => {
-    const resultsHeader = 'Results';
-    loadTimeData.overrideValues({
-      successTitleSingle: resultsHeader,
-    });
-    await autoTabGroupsPageSetup();
-
-    const announcementPromise =
-        eventToPromise('cr-a11y-announcer-messages-sent', document.body);
-    testApiProxy.getCallbackRouterRemote().tabOrganizationSessionUpdated(
-        createSession({state: TabOrganizationState.kSuccess}));
-
-    const announcement = await announcementPromise;
-    assertTrue(!!announcement);
-    assertTrue(announcement.detail.messages.includes(resultsHeader));
-  });
-
-  test('Announces failure header on state change', async () => {
-    const failureHeader = 'Failure';
-    loadTimeData.overrideValues({
-      failureTitleGeneric: failureHeader,
-    });
-    await autoTabGroupsPageSetup();
-
-    const announcementPromise =
-        eventToPromise('cr-a11y-announcer-messages-sent', document.body);
-    testApiProxy.getCallbackRouterRemote().tabOrganizationSessionUpdated(
-        createSession({
-          state: TabOrganizationState.kFailure,
-          error: TabOrganizationError.kGeneric,
-        }));
-
-    const announcement = await announcementPromise;
-    assertTrue(!!announcement);
-    assertTrue(announcement.detail.messages.includes(failureHeader));
-  });
 });
diff --git a/chrome/test/data/webui/webview/webui_webview_browsertest.cc b/chrome/test/data/webui/webview/webui_webview_browsertest.cc
index f530af82..14867d8 100644
--- a/chrome/test/data/webui/webview/webui_webview_browsertest.cc
+++ b/chrome/test/data/webui/webview/webui_webview_browsertest.cc
@@ -33,7 +33,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_switches.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
-#include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
 #endif
 
diff --git a/chromeos/ash/components/boca/BUILD.gn b/chromeos/ash/components/boca/BUILD.gn
index 51f76ea..f00d239 100644
--- a/chromeos/ash/components/boca/BUILD.gn
+++ b/chromeos/ash/components/boca/BUILD.gn
@@ -14,6 +14,8 @@
     "boca_role_util.h",
     "boca_session_manager.cc",
     "boca_session_manager.h",
+    "boca_session_util.cc",
+    "boca_session_util.h",
   ]
   deps = [
     "//ash",
@@ -31,7 +33,10 @@
 source_set("unit_tests") {
   testonly = true
 
-  sources = [ "boca_session_manager_unittest.cc" ]
+  sources = [
+    "boca_session_manager_unittest.cc",
+    "boca_session_util_unittest.cc",
+  ]
 
   deps = [
     ":boca",
diff --git a/chromeos/ash/components/boca/boca_session_manager.cc b/chromeos/ash/components/boca/boca_session_manager.cc
index 3bb2a4e..e0d89fd 100644
--- a/chromeos/ash/components/boca/boca_session_manager.cc
+++ b/chromeos/ash/components/boca/boca_session_manager.cc
@@ -13,6 +13,7 @@
 #include "ash/shell.h"
 #include "base/time/time.h"
 #include "chromeos/ash/components/boca/boca_app_client.h"
+#include "chromeos/ash/components/boca/boca_session_util.h"
 #include "chromeos/ash/components/boca/proto/bundle.pb.h"
 #include "chromeos/ash/components/boca/proto/roster.pb.h"
 #include "chromeos/ash/components/boca/proto/session.pb.h"
@@ -23,35 +24,6 @@
 #include "google_apis/common/api_error_codes.h"
 
 namespace ash::boca {
-namespace {
-
-::boca::SessionConfig GetSessionConfigSafe(::boca::Session* session) {
-  if (!session) {
-    return ::boca::SessionConfig();
-  }
-  if (session->student_group_configs().empty()) {
-    return ::boca::SessionConfig();
-  }
-  auto it = session->student_group_configs().find(kMainStudentGroupName);
-  if (it == session->student_group_configs().end()) {
-    return ::boca::SessionConfig();
-  }
-  return it->second;
-}
-
-google::protobuf::RepeatedPtrField<::boca::UserIdentity> GetStudentGroupsSafe(
-    ::boca::Session* session) {
-  if (!session || session->roster().student_groups().empty()) {
-    return google::protobuf::RepeatedPtrField<::boca::UserIdentity>();
-  }
-  return session->roster().student_groups()[0].students();
-}
-
-::boca::Roster GetRosterSafe(::boca::Session* session) {
-  return session ? session->roster() : ::boca::Roster();
-}
-
-}  // namespace
 
 BocaSessionManager::BocaSessionManager(SessionClientImpl* session_client_impl,
                                        AccountId account_id)
diff --git a/chromeos/ash/components/boca/boca_session_util.cc b/chromeos/ash/components/boca/boca_session_util.cc
new file mode 100644
index 0000000..ab71dd7e
--- /dev/null
+++ b/chromeos/ash/components/boca/boca_session_util.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 "chromeos/ash/components/boca/boca_session_util.h"
+
+#include "chromeos/ash/components/boca/proto/session.pb.h"
+#include "chromeos/ash/components/boca/session_api/constants.h"
+
+namespace ash::boca {
+::boca::SessionConfig GetSessionConfigSafe(::boca::Session* session) {
+  if (!session) {
+    return ::boca::SessionConfig();
+  }
+  if (session->student_group_configs().empty()) {
+    return ::boca::SessionConfig();
+  }
+  auto it = session->student_group_configs().find(kMainStudentGroupName);
+  if (it == session->student_group_configs().end()) {
+    return ::boca::SessionConfig();
+  }
+  return it->second;
+}
+
+google::protobuf::RepeatedPtrField<::boca::UserIdentity> GetStudentGroupsSafe(
+    ::boca::Session* session) {
+  if (!session || session->roster().student_groups().empty()) {
+    return google::protobuf::RepeatedPtrField<::boca::UserIdentity>();
+  }
+  return session->roster().student_groups()[0].students();
+}
+
+::boca::Roster GetRosterSafe(::boca::Session* session) {
+  return session ? session->roster() : ::boca::Roster();
+}
+}  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/boca_session_util.h b/chromeos/ash/components/boca/boca_session_util.h
new file mode 100644
index 0000000..338a4adc
--- /dev/null
+++ b/chromeos/ash/components/boca/boca_session_util.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 CHROMEOS_ASH_COMPONENTS_BOCA_BOCA_SESSION_UTIL_H_
+#define CHROMEOS_ASH_COMPONENTS_BOCA_BOCA_SESSION_UTIL_H_
+
+#include "ash/ash_export.h"
+#include "chromeos/ash/components/boca/proto/session.pb.h"
+
+namespace ash::boca {
+
+ASH_EXPORT ::boca::SessionConfig GetSessionConfigSafe(::boca::Session* session);
+
+ASH_EXPORT google::protobuf::RepeatedPtrField<::boca::UserIdentity>
+GetStudentGroupsSafe(::boca::Session* session);
+
+ASH_EXPORT ::boca::Roster GetRosterSafe(::boca::Session* session);
+
+}  // namespace ash::boca
+
+#endif  // CHROMEOS_ASH_COMPONENTS_BOCA_BOCA_SESSION_UTIL_H_
diff --git a/chromeos/ash/components/boca/boca_session_util_unittest.cc b/chromeos/ash/components/boca/boca_session_util_unittest.cc
new file mode 100644
index 0000000..274638b
--- /dev/null
+++ b/chromeos/ash/components/boca/boca_session_util_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/boca/boca_session_util.h"
+
+#include "chromeos/ash/components/boca/proto/roster.pb.h"
+#include "chromeos/ash/components/boca/session_api/constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash::boca {
+namespace {
+
+class BocaSessionUtilTest : public testing::Test {
+ protected:
+  BocaSessionUtilTest() = default;
+};
+
+TEST_F(BocaSessionUtilTest, TestGetSessionConfigWithNullInputShouldNotCrash) {
+  ASSERT_EQ(::boca::SessionConfig().SerializeAsString(),
+            GetSessionConfigSafe(nullptr).SerializeAsString());
+  ::boca::Session session;
+  ASSERT_EQ(::boca::SessionConfig().SerializeAsString(),
+            GetSessionConfigSafe(&session).SerializeAsString());
+  session.mutable_student_group_configs()->emplace("test",
+                                                   ::boca::SessionConfig());
+  ASSERT_EQ(::boca::SessionConfig().SerializeAsString(),
+            GetSessionConfigSafe(&session).SerializeAsString());
+
+  auto session_config = ::boca::SessionConfig();
+  session_config.mutable_captions_config()->set_captions_enabled(true);
+  session.mutable_student_group_configs()->emplace(kMainStudentGroupName,
+                                                   session_config);
+
+  ASSERT_EQ(session_config.SerializeAsString(),
+            GetSessionConfigSafe(&session).SerializeAsString());
+}
+
+TEST_F(BocaSessionUtilTest, TestGetStudentGroupsWithNullInputShouldNotCrash) {
+  ASSERT_EQ(0, GetStudentGroupsSafe(nullptr).size());
+
+  ::boca::Session session;
+  ASSERT_EQ(0, GetStudentGroupsSafe(&session).size());
+
+  auto* student_groups =
+      session.mutable_roster()->mutable_student_groups()->Add();
+  student_groups->set_title("main");
+  auto* student = student_groups->mutable_students()->Add();
+  student->set_email("test");
+  ASSERT_EQ(1, GetStudentGroupsSafe(&session).size());
+}
+
+TEST_F(BocaSessionUtilTest, TestGetRosterWithNullInputShouldNotCrash) {
+  ASSERT_EQ(::boca::Roster().SerializeAsString(),
+            GetRosterSafe(nullptr).SerializeAsString());
+
+  ::boca::Session session;
+  session.mutable_roster()->set_roster_id("123");
+  ASSERT_EQ("123", GetRosterSafe(&session).roster_id());
+}
+}  // namespace
+}  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/session_api/get_session_request.h b/chromeos/ash/components/boca/session_api/get_session_request.h
index 03a99f3..afc9c024 100644
--- a/chromeos/ash/components/boca/session_api/get_session_request.h
+++ b/chromeos/ash/components/boca/session_api/get_session_request.h
@@ -30,8 +30,10 @@
   GetSessionRequest(const GetSessionRequest&) = delete;
   GetSessionRequest& operator=(const GetSessionRequest&) = delete;
   ~GetSessionRequest() override;
+
   // For testing.
   void OverrideURLForTesting(std::string url);
+  Callback callback() { return std::move(callback_); }
 
  protected:
   // UrlFetchRequestBase:
diff --git a/chromeos/ash/components/network/auto_connect_handler_unittest.cc b/chromeos/ash/components/network/auto_connect_handler_unittest.cc
index a294a07..2f78268 100644
--- a/chromeos/ash/components/network/auto_connect_handler_unittest.cc
+++ b/chromeos/ash/components/network/auto_connect_handler_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "chromeos/ash/components/network/client_cert_resolver.h"
 #include "chromeos/ash/components/network/managed_network_configuration_handler_impl.h"
@@ -298,6 +299,7 @@
   NetworkStateTestHelper& helper() { return helper_; }
 
   base::test::TaskEnvironment task_environment_;
+  base::test::ScopedFeatureList scoped_feature_list_;
   NetworkStateTestHelper helper_{/*use_default_devices_and_services=*/false};
   std::unique_ptr<AutoConnectHandler> auto_connect_handler_;
   std::unique_ptr<ClientCertResolver> client_cert_resolver_;
@@ -713,6 +715,8 @@
 
 TEST_F(AutoConnectHandlerTest,
        DisableCellularAutoConnectOnAllowOnlyPolicyNetworksAutoconnect) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   base::HistogramTester histogram_tester;
   std::string cellular1_service_path =
       ConfigureService(kConfigureCellular1UnmanagedConnected);
@@ -762,6 +766,8 @@
 
 TEST_F(AutoConnectHandlerTest,
        DisconnectCellularOnPolicyLoadingAllowOnlyPolicyCellularNetworks) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   base::HistogramTester histogram_tester;
   std::string cellular1_service_path =
       ConfigureService(kConfigureCellular1UnmanagedConnected);
diff --git a/chromeos/ash/components/network/metrics/esim_policy_login_metrics_logger_unittest.cc b/chromeos/ash/components/network/metrics/esim_policy_login_metrics_logger_unittest.cc
index 84dc2fe4..53aa9c4 100644
--- a/chromeos/ash/components/network/metrics/esim_policy_login_metrics_logger_unittest.cc
+++ b/chromeos/ash/components/network/metrics/esim_policy_login_metrics_logger_unittest.cc
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <memory>
-
 #include "chromeos/ash/components/network/metrics/esim_policy_login_metrics_logger.h"
 
+#include <memory>
+
 #include "ash/constants/ash_features.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "chromeos/ash/components/login/login_state/login_state.h"
@@ -117,6 +118,7 @@
   }
 
   base::test::TaskEnvironment task_environment_;
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::HistogramTester histogram_tester_;
   NetworkStateTestHelper network_state_test_helper_{
       /*use_default_devices_and_services=*/false};
@@ -129,6 +131,8 @@
 };
 
 TEST_F(ESimPolicyLoginMetricsLoggerTest, LoginMetricsTest) {
+  scoped_feature_list_.InitAndDisableFeature(
+      ash::features::kAllowApnModificationPolicy);
   // Perform this test as though this "device" is enterprise managed.
   esim_policy_login_metrics_logger_->SetIsEnterpriseManaged(
       /*is_enterprise_managed=*/true);
diff --git a/chromeos/ash/services/network_config/cros_network_config_unittest.cc b/chromeos/ash/services/network_config/cros_network_config_unittest.cc
index f06ca27..7eba60c 100644
--- a/chromeos/ash/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/ash/services/network_config/cros_network_config_unittest.cc
@@ -4258,7 +4258,7 @@
   base::RunLoop().RunUntilIdle();
   mojom::GlobalPolicyPtr policy = GetGlobalPolicy();
   ASSERT_TRUE(policy);
-  EXPECT_TRUE(policy->allow_apn_modification);
+  EXPECT_FALSE(policy->allow_apn_modification);
   EXPECT_FALSE(policy->allow_cellular_sim_lock);
   EXPECT_FALSE(policy->allow_cellular_hotspot);
   EXPECT_TRUE(policy->allow_only_policy_cellular_networks);
@@ -4274,14 +4274,6 @@
   EXPECT_EQ(1, observer()->GetPolicyAppliedCount(/*userhash=*/std::string()));
 
   policy = GetGlobalPolicy();
-  EXPECT_TRUE(policy->allow_apn_modification);
-
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(/*enabled_features=*/
-                                       {features::kApnRevamp,
-                                        features::kAllowApnModificationPolicy},
-                                       /*disabled_features=*/{});
-  policy = GetGlobalPolicy();
   EXPECT_FALSE(policy->allow_apn_modification);
 }
 
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 430809b..ba370d8 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -177,6 +177,9 @@
 BASE_FEATURE(kMahiSendingUrl,
              "MahiSendingUrl",
              base::FEATURE_ENABLED_BY_DEFAULT);
+
+// Controls whether to enable Mahi for managed users.
+BASE_FEATURE(kMahiManaged, "MahiManaged", base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Controls enabling / disabling the sparky feature.
@@ -472,6 +475,14 @@
 #endif
 }
 
+bool IsMahiManagedEnabled() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  return base::FeatureList::IsEnabled(kMahiManaged);
+#else
+  return false;
+#endif
+}
+
 bool IsSparkyEnabled() {
   return base::FeatureList::IsEnabled(kSparky);
 }
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 65d2057..ae4c5df9 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -72,6 +72,7 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 BASE_DECLARE_FEATURE(kFeatureManagementMahi);
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kMahiSendingUrl);
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kMahiManaged);
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kSparky);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kMahiDebugging);
@@ -153,6 +154,7 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsJellyrollEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsMahiEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsMahiSendingUrl();
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsMahiManagedEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsMahiDebuggingEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsNotificationWidthIncreaseEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsRoundedWindowsEnabled();
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn
index 02a2687..d0ac1e1 100644
--- a/chromeos/crosapi/mojom/BUILD.gn
+++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -170,6 +170,7 @@
     "//ui/accessibility:ax_enums_mojo",
     "//ui/accessibility/mojom",
     "//ui/base/mojom",
+    "//ui/base/mojom:ui_base_types",
     "//ui/color:mojom",
     "//ui/display/mojom:mojom",
     "//ui/events/mojom:mojom",
diff --git a/chromeos/crosapi/mojom/desk_template.mojom b/chromeos/crosapi/mojom/desk_template.mojom
index 72c85ae..9c3d620 100644
--- a/chromeos/crosapi/mojom/desk_template.mojom
+++ b/chromeos/crosapi/mojom/desk_template.mojom
@@ -5,7 +5,7 @@
 module crosapi.mojom;
 
 import "components/tab_groups/public/mojom/tab_group_types.mojom";
-import "ui/base/mojom/ui_base_types.mojom";
+import "ui/base/mojom/window_show_state.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 import "url/mojom/url.mojom";
 import "ui/gfx/image/mojom/image.mojom";
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 9d10b168..ace9287 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-130-6668.28-1725850545-benchmark-130.0.6710.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-130-6668.28-1725850545-benchmark-130.0.6711.0-r1-redacted.afdo.xz
diff --git a/clank b/clank
index b0adcc5..07fe1b4 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit b0adcc518a532ef53db88a1239c211f4bd4d85e5
+Subproject commit 07fe1b4bc66d1a5407385d6699e86106cb551735
diff --git a/codelabs/cpp101/solutions/factor.cc b/codelabs/cpp101/solutions/factor.cc
index 2151b082..fd7ccbb 100644
--- a/codelabs/cpp101/solutions/factor.cc
+++ b/codelabs/cpp101/solutions/factor.cc
@@ -54,7 +54,7 @@
   base::test::TaskEnvironment task_environment{
       base::test::TaskEnvironment::TimeSource::SYSTEM_TIME};
 
-  if (argc <= 1) {
+  if (argc < 2) {
     LOG(INFO) << argv[0] << ": missing operand\n";
     return -1;
   }
diff --git a/codelabs/cpp101/solutions/fibonacci.cc b/codelabs/cpp101/solutions/fibonacci.cc
index 61e9e58c..0234b9e 100644
--- a/codelabs/cpp101/solutions/fibonacci.cc
+++ b/codelabs/cpp101/solutions/fibonacci.cc
@@ -33,7 +33,7 @@
 }  // namespace
 
 int main(int argc, char* argv[]) {
-  if (argc <= 1) {
+  if (argc < 2) {
     LOG(INFO) << argv[0] << ": missing operand";
     return -1;
   }
@@ -59,4 +59,4 @@
   }
 
   return 0;
-}
\ No newline at end of file
+}
diff --git a/codelabs/cpp101/solutions/mojo.cc b/codelabs/cpp101/solutions/mojo.cc
index 92ba4e95..55a78061 100644
--- a/codelabs/cpp101/solutions/mojo.cc
+++ b/codelabs/cpp101/solutions/mojo.cc
@@ -24,7 +24,7 @@
       base::test::TaskEnvironment::TimeSource::SYSTEM_TIME};
   mojo::core::Init();
 
-  if (argc <= 2) {
+  if (argc < 3) {
     LOG(INFO) << argv[0] << ": missing operand";
     return -1;
   }
diff --git a/codelabs/cpp101/solutions/sleep.cc b/codelabs/cpp101/solutions/sleep.cc
index 3c1ac73..b82310eb 100644
--- a/codelabs/cpp101/solutions/sleep.cc
+++ b/codelabs/cpp101/solutions/sleep.cc
@@ -20,7 +20,7 @@
   base::test::TaskEnvironment task_environment{
       base::test::TaskEnvironment::TimeSource::SYSTEM_TIME};
 
-  if (argc <= 1) {
+  if (argc < 2) {
     LOG(INFO) << argv[0] << ": missing operand";
     return -1;
   }
diff --git a/components/affiliations/core/browser/affiliation_utils.h b/components/affiliations/core/browser/affiliation_utils.h
index fd21b83..b6c0354 100644
--- a/components/affiliations/core/browser/affiliation_utils.h
+++ b/components/affiliations/core/browser/affiliation_utils.h
@@ -87,7 +87,9 @@
 
   // As a light-weight std::string wrapper, allow copy and assign.
   FacetURI(const FacetURI&) = default;
+  FacetURI(FacetURI&&) = default;
   FacetURI& operator=(const FacetURI&) = default;
+  FacetURI& operator=(FacetURI&&) = default;
 
   friend std::weak_ordering operator<=>(const FacetURI& lhs,
                                         const FacetURI& rhs) {
diff --git a/components/app_restore/app_restore_data.cc b/components/app_restore/app_restore_data.cc
index 75e6105..54a48db 100644
--- a/components/app_restore/app_restore_data.cc
+++ b/components/app_restore/app_restore_data.cc
@@ -11,6 +11,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "components/app_restore/app_launch_info.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 
 namespace app_restore {
 
@@ -216,10 +217,10 @@
              : std::nullopt;
 }
 
-std::optional<ui::WindowShowState> GetPreMinimizedShowStateTypeFromDict(
+std::optional<ui::mojom::WindowShowState> GetPreMinimizedShowStateTypeFromDict(
     const base::Value::Dict& dict) {
   return dict.Find(kPreMinimizedShowStateTypeKey)
-             ? std::make_optional(static_cast<ui::WindowShowState>(
+             ? std::make_optional(static_cast<ui::mojom::WindowShowState>(
                    dict.FindInt(kPreMinimizedShowStateTypeKey).value()))
              : std::nullopt;
 }
diff --git a/components/app_restore/restore_data_unittest.cc b/components/app_restore/restore_data_unittest.cc
index b43b2af..820d8fb6 100644
--- a/components/app_restore/restore_data_unittest.cc
+++ b/components/app_restore/restore_data_unittest.cc
@@ -24,6 +24,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -86,12 +87,12 @@
 constexpr chromeos::WindowStateType kWindowStateType3 =
     chromeos::WindowStateType::kPrimarySnapped;
 
-constexpr ui::WindowShowState kPreMinimizedWindowStateType1 =
-    ui::SHOW_STATE_DEFAULT;
-constexpr ui::WindowShowState kPreMinimizedWindowStateType2 =
-    ui::SHOW_STATE_MAXIMIZED;
-constexpr ui::WindowShowState kPreMinimizedWindowStateType3 =
-    ui::SHOW_STATE_DEFAULT;
+constexpr ui::mojom::WindowShowState kPreMinimizedWindowStateType1 =
+    ui::mojom::WindowShowState::kDefault;
+constexpr ui::mojom::WindowShowState kPreMinimizedWindowStateType2 =
+    ui::mojom::WindowShowState::kMaximized;
+constexpr ui::mojom::WindowShowState kPreMinimizedWindowStateType3 =
+    ui::mojom::WindowShowState::kDefault;
 
 constexpr int32_t kSnapPercentage = 75;
 
@@ -246,7 +247,7 @@
       const base::Uuid& desk_guid,
       const gfx::Rect& current_bounds,
       chromeos::WindowStateType window_state_type,
-      ui::WindowShowState pre_minimized_show_state_type,
+      ui::mojom::WindowShowState pre_minimized_show_state_type,
       uint32_t snap_percentage,
       std::optional<gfx::Size> max_size,
       std::optional<gfx::Size> min_size,
diff --git a/components/app_restore/window_info.cc b/components/app_restore/window_info.cc
index 1c18b07..a5f7a44 100644
--- a/components/app_restore/window_info.cc
+++ b/components/app_restore/window_info.cc
@@ -6,6 +6,7 @@
 
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 
 namespace app_restore {
 
@@ -28,7 +29,7 @@
   return ToPrefixedString(new_val, prefix);
 }
 
-std::string ToPrefixedString(std::optional<ui::WindowShowState> val,
+std::string ToPrefixedString(std::optional<ui::mojom::WindowShowState> val,
                              const std::string& prefix) {
   std::optional<int> new_val =
       val ? std::make_optional(static_cast<int32_t>(*val)) : std::nullopt;
diff --git a/components/app_restore/window_info.h b/components/app_restore/window_info.h
index 29a1726..e9beaf91 100644
--- a/components/app_restore/window_info.h
+++ b/components/app_restore/window_info.h
@@ -12,6 +12,7 @@
 #include "chromeos/ui/base/window_state_type.h"
 #include "components/tab_groups/tab_group_info.h"
 #include "ui/aura/window.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/geometry/rect.h"
 #include "url/gurl.h"
@@ -88,7 +89,7 @@
 
   // Show state of a window before it was minimized. Empty for non-minimized
   // windows.
-  std::optional<ui::WindowShowState> pre_minimized_show_state_type;
+  std::optional<ui::mojom::WindowShowState> pre_minimized_show_state_type;
 
   // The snap percentage of a window, if it is snapped. For instance a snap
   // percentage of 75 means the window takes up three quarters of the work area.
diff --git a/components/autofill/android/BUILD.gn b/components/autofill/android/BUILD.gn
index 8086b63..7f9f81e 100644
--- a/components/autofill/android/BUILD.gn
+++ b/components/autofill/android/BUILD.gn
@@ -186,6 +186,7 @@
     "../core/browser/metrics/payments/mandatory_reauth_metrics.h",
     "../core/browser/metrics/payments/virtual_card_enrollment_metrics.h",
     "../core/browser/payments/card_unmask_challenge_option.h",
+    "../core/browser/ui/autofill_image_fetcher_base.h",
     "../core/browser/ui/payments/payments_bubble_closed_reasons.h",
     "../core/browser/ui/suggestion.h",
     "../core/browser/ui/suggestion_type.h",
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 77894ac..6234cd5 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -1224,6 +1224,7 @@
     "metrics/stored_profile_metrics_unittest.cc",
     "metrics/suggestions_list_metrics_unittest.cc",
     "payments/autofill_offer_manager_unittest.cc",
+    "payments/credit_card_access_manager_risk_based_unittest.cc",
     "payments/credit_card_access_manager_unittest.cc",
     "payments/credit_card_cvc_authenticator_unittest.cc",
     "payments/credit_card_otp_authenticator_unittest.cc",
diff --git a/components/autofill/core/browser/autofill_prediction_improvements_delegate.h b/components/autofill/core/browser/autofill_prediction_improvements_delegate.h
index 0886e6c..1b49364 100644
--- a/components/autofill/core/browser/autofill_prediction_improvements_delegate.h
+++ b/components/autofill/core/browser/autofill_prediction_improvements_delegate.h
@@ -14,6 +14,7 @@
 
 class FormData;
 class FormFieldData;
+class FormStructure;
 
 // The interface for communication from //components/autofill to
 // //components/autofill/autofill_prediction_improvements.
@@ -36,6 +37,9 @@
       const FormFieldData& field,
       bool should_add_trigger_suggestion) = 0;
 
+  // Returns whether `form` is eligible for the improved prediction experience.
+  virtual bool IsFormEligible(const FormStructure& form) = 0;
+
   // Returns `true` if the corresponding feature is enabled and optimization can
   // be applied.
   virtual bool ShouldProvidePredictionImprovements(const GURL& url) = 0;
diff --git a/components/autofill/core/browser/filling_product.cc b/components/autofill/core/browser/filling_product.cc
index 9975c35..2b66f358 100644
--- a/components/autofill/core/browser/filling_product.cc
+++ b/components/autofill/core/browser/filling_product.cc
@@ -11,6 +11,7 @@
 
 namespace autofill {
 
+// LINT.IfChange
 std::string FillingProductToString(FillingProduct filling_product) {
   switch (filling_product) {
     case FillingProduct::kNone:
@@ -32,12 +33,16 @@
     case FillingProduct::kPlusAddresses:
       return "PlusAddresses";
     case FillingProduct::kStandaloneCvc:
-      return "VirtualCard.StandaloneCvc";
+      return "StandaloneCvc";
     case FillingProduct::kPredictionImprovements:
       return "PredictionImprovements";
   };
   NOTREACHED();
 }
+// LINT.ThenChange(
+//   /tools/metrics/histograms/metadata/autofill/histograms.xml:Autofill.FillingProduct,
+//   /tools/metrics/histograms/metadata/autofill/histograms.xml:Autofill.FillingProduct.Condensed
+// )
 
 FillingProduct GetFillingProductFromSuggestionType(SuggestionType type) {
   switch (type) {
diff --git a/components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.cc b/components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.cc
index 52285af..6579856 100644
--- a/components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.cc
+++ b/components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.cc
@@ -55,6 +55,9 @@
     case ServerCardUnmaskFlowType::kDeviceUnlock:
       flow_type_suffix = ".DeviceUnlock";
       break;
+    case ServerCardUnmaskFlowType::kThreeDomainSecure:
+      flow_type_suffix = ".ThreeDomainSecure";
+      break;
   }
 
   base::UmaHistogramEnumeration(
diff --git a/components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.h b/components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.h
index ab2bf44..577c798 100644
--- a/components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.h
+++ b/components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.h
@@ -70,7 +70,9 @@
   kRiskBased = 4,
   // Device unlock auth was offered.
   kDeviceUnlock = 5,
-  kMaxValue = kDeviceUnlock,
+  // VCN 3DS auth was offered.
+  kThreeDomainSecure = 6,
+  kMaxValue = kThreeDomainSecure,
 };
 
 void LogServerCardUnmaskAttempt(
diff --git a/components/autofill/core/browser/mock_autofill_prediction_improvements_delegate.h b/components/autofill/core/browser/mock_autofill_prediction_improvements_delegate.h
index bbb3f72..3e123e1 100644
--- a/components/autofill/core/browser/mock_autofill_prediction_improvements_delegate.h
+++ b/components/autofill/core/browser/mock_autofill_prediction_improvements_delegate.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_AUTOFILL_PREDICTION_IMPROVEMENTS_DELEGATE_H_
 
 #include "components/autofill/core/browser/autofill_prediction_improvements_delegate.h"
+#include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
@@ -33,6 +34,10 @@
               UserFeedbackReceived,
               (AutofillPredictionImprovementsDelegate::UserFeedback feedback),
               (override));
+  MOCK_METHOD(bool,
+              IsFormEligible,
+              (const autofill::FormStructure& form),
+              (override));
   MOCK_METHOD(void, UserClickedLearnMore, (), (override));
   MOCK_METHOD(void,
               OnClickedTriggerSuggestion,
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 3933744..83ff693f 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -1760,14 +1760,28 @@
 
 void CreditCardAccessManager::OnVcn3dsAuthenticationComplete(
     payments::PaymentsWindowManager::Vcn3dsAuthenticationResponse response) {
-  if (response.card.has_value()) {
+  if (response.result ==
+      payments::PaymentsWindowManager::Vcn3dsAuthenticationResult::kSuccess) {
+    CHECK(response.card.has_value());
     // `on_credit_card_fetched_callback_` makes a copy of `card` and `cvc`
     // before it asynchronously fills them into the form. Thus it is safe to
     // pass the address of `response.card.value()` here, as by the time it goes
     // out of scope, a copy will have already been made to fill the form.
     std::move(on_credit_card_fetched_callback_)
         .Run(CreditCardFetchResult::kSuccess, &response.card.value());
+    autofill_metrics::LogServerCardUnmaskResult(
+        autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked,
+        PaymentsRpcCardType::kVirtualCard,
+        autofill_metrics::ServerCardUnmaskFlowType::kThreeDomainSecure);
   } else {
+    autofill_metrics::LogServerCardUnmaskResult(
+        response.result == payments::PaymentsWindowManager::
+                               Vcn3dsAuthenticationResult::kAuthenticationFailed
+            ? autofill_metrics::ServerCardUnmaskResult::
+                  kVirtualCardRetrievalError
+            : autofill_metrics::ServerCardUnmaskResult::kFlowCancelled,
+        PaymentsRpcCardType::kVirtualCard,
+        autofill_metrics::ServerCardUnmaskFlowType::kThreeDomainSecure);
     std::move(on_credit_card_fetched_callback_)
         .Run(CreditCardFetchResult::kTransientError, nullptr);
   }
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_risk_based_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_risk_based_unittest.cc
new file mode 100644
index 0000000..c40958b
--- /dev/null
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_risk_based_unittest.cc
@@ -0,0 +1,563 @@
+// 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/functional/bind.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/form_data_importer_test_api.h"
+#include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h"
+#include "components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.h"
+#include "components/autofill/core/browser/payments/credit_card_access_manager.h"
+#include "components/autofill/core/browser/payments/credit_card_access_manager_test_base.h"
+#include "components/autofill/core/browser/payments/credit_card_risk_based_authenticator.h"
+#include "components/autofill/core/browser/payments/mandatory_reauth_manager.h"
+#include "components/autofill/core/browser/payments/payments_autofill_client.h"
+
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
+#include "components/autofill/core/browser/metrics/payments/better_auth_metrics.h"
+#include "components/autofill/core/browser/payments/credit_card_access_manager_test_api.h"
+#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
+#endif
+
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/build_info.h"
+#endif
+
+namespace autofill {
+namespace {
+
+using PaymentsRpcResult = payments::PaymentsAutofillClient::PaymentsRpcResult;
+using autofill_metrics::CreditCardFormEventLogger;
+
+class CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest
+    : public CreditCardAccessManagerTestBase {
+ public:
+  CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest() = default;
+  ~CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest() override =
+      default;
+
+  base::test::ScopedFeatureList feature_list_{
+      features::kAutofillEnableFpanRiskBasedAuthentication};
+
+  void MockRiskBasedAuthSucceedsWithoutPanReturned(
+      CreditCard* card,
+      std::string context_token = "fake_context_token") {
+    credit_card_access_manager().FetchCreditCard(
+        card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                             accessor_->GetWeakPtr()));
+
+    // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
+    // invoked.
+    EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                    ->risk_based_authentication_invoked());
+    EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                    ->autofill_progress_dialog_shown());
+
+    // Mock that
+    // CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
+    // indicates a yellow path with context token returned.
+    credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+        CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+            .with_result(CreditCardRiskBasedAuthenticator::
+                             RiskBasedAuthenticationResponse::Result::
+                                 kAuthenticationRequired)
+            .with_context_token(context_token));
+  }
+};
+
+// Test the flow when the masked server card is successfully returned from
+// the server during a risk-based retrieval.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       RiskBasedMaskedServerCardUnmasking_Success) {
+#if BUILDFLAG(IS_ANDROID)
+  if (base::android::BuildInfo::GetInstance()->is_automotive()) {
+    GTEST_SKIP() << "This test should not run on automotive.";
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  base::HistogramTester histogram_tester;
+  std::string test_number = "4444333322221111";
+  CreditCard* masked_server_card =
+      CreateServerCard(kTestGUID, test_number, kTestServerId);
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
+  // invoked.
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->risk_based_authentication_invoked());
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->autofill_progress_dialog_shown());
+
+  CreditCard card = *masked_server_card;
+  card.set_record_type(CreditCard::RecordType::kFullServerCard);
+  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
+  // indicates a green path with valid card number returned.
+  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+          .with_result(CreditCardRiskBasedAuthenticator::
+                           RiskBasedAuthenticationResponse::Result::
+                               kNoAuthenticationRequired)
+          .with_card(card));
+
+  // Ensure the accessor received the correct response.
+  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
+  EXPECT_EQ(accessor_->number(), base::UTF8ToUTF16(test_number));
+
+  // There was no interactive authentication in this flow, so check that this
+  // is signaled correctly.
+  std::optional<NonInteractivePaymentMethodType> type =
+      test_api(*autofill_client_.GetFormDataImporter())
+          .payment_method_type_if_non_interactive_authentication_flow_completed();
+  EXPECT_THAT(type, testing::Optional(
+                        NonInteractivePaymentMethodType::kMaskedServerCard));
+
+  // Expect the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ServerCardUnmask.ServerCard.Result.RiskBased",
+      autofill_metrics::ServerCardUnmaskResult::kRiskBasedUnmasked, 1);
+}
+
+// Ensures that the masked server card risk-based unmasking response is
+// handled correctly if the retrieval failed.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       RiskBasedMaskedServerCardUnmasking_RetrievalError) {
+  base::HistogramTester histogram_tester;
+  CreditCard* masked_server_card =
+      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
+  // invoked.
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->risk_based_authentication_invoked());
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->autofill_progress_dialog_shown());
+
+  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
+  // indicates a red path.
+  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+          .with_result(CreditCardRiskBasedAuthenticator::
+                           RiskBasedAuthenticationResponse::Result::kError));
+
+  // Expect the CreditCardAccessManager to end the session.
+  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->autofill_error_dialog_shown());
+
+  // Expect the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ServerCardUnmask.ServerCard.Result.RiskBased",
+      autofill_metrics::ServerCardUnmaskResult::kUnexpectedError, 1);
+}
+
+// Ensures that the masked server card risk-based unmasking response is
+// handled correctly if the flow is cancelled.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       RiskBasedMaskedServerCardUnmasking_FlowCancelled) {
+  base::HistogramTester histogram_tester;
+  CreditCard* masked_server_card =
+      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
+  // invoked.
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->risk_based_authentication_invoked());
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->autofill_progress_dialog_shown());
+
+  // Mock the authentication is cancelled.
+  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+          .with_result(CreditCardRiskBasedAuthenticator::
+                           RiskBasedAuthenticationResponse::Result::
+                               kAuthenticationCancelled));
+
+  // Expect the CreditCardAccessManager to end the session.
+  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
+
+  // Expect the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ServerCardUnmask.ServerCard.Result.RiskBased",
+      autofill_metrics::ServerCardUnmaskResult::kFlowCancelled, 1);
+}
+
+// Ensures that the masked server card risk-based authentication is not invoked
+// when the feature is disabled.
+TEST_F(
+    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+    RiskBasedMaskedServerCardUnmasking_RiskBasedAuthenticationNotInvoked_FeatureDisabled) {
+  feature_list_.Reset();
+  feature_list_.InitAndDisableFeature(
+      features::kAutofillEnableFpanRiskBasedAuthentication);
+  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
+  CreditCard* masked_server_card =
+      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is not invoked.
+  ASSERT_FALSE(autofill_client_.GetPaymentsAutofillClient()
+                   ->risk_based_authentication_invoked());
+}
+
+TEST_F(
+    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+    RiskBasedMaskedServerCardUnmasking_CvcAuthenticationRequired_ContextTokenSetCorrectly) {
+  std::string context_token = "context_token";
+  CreditCard* masked_server_card =
+      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
+
+  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card,
+                                              context_token);
+
+  // Expect the context_token is set in the full card request.
+  EXPECT_EQ(GetCvcAuthenticator()
+                .GetFullCardRequest()
+                ->GetUnmaskRequestDetailsForTesting()
+                ->context_token,
+            context_token);
+}
+
+// Ensures the authentication is delegated to the CVC authenticator when
+// `fido_request_options` is not returned.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_CvcOnly) {
+  base::HistogramTester histogram_tester;
+
+  std::string test_number = "4444333322221111";
+  CreditCard* masked_server_card =
+      CreateServerCard(kTestGUID, test_number, kTestServerId);
+
+  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.BetterAuth.FlowEvents.Cvc",
+      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
+
+  // Expect CVC prompt to be invoked.
+  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, test_number));
+  // Ensure that the form is filled.
+  EXPECT_EQ(base::UTF8ToUTF16(test_number), accessor_->number());
+  EXPECT_EQ(kTestCvc16, accessor_->cvc());
+
+  // Expect that we did not signal that there was no interactive authentication.
+  EXPECT_FALSE(
+      test_api(*autofill_client_.GetFormDataImporter())
+          .payment_method_type_if_non_interactive_authentication_flow_completed()
+          .has_value());
+}
+
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
+// Ensures the masked server card risk-based unmasking response is handled
+// correctly and authentication is delegated to the FIDO authenticator, when
+// `fido_request_options` is returned.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_FidoOnly) {
+  base::HistogramTester histogram_tester;
+
+  std::string test_number = "4444333322221111";
+  CreditCard* masked_server_card =
+      CreateServerCard(kTestGUID, test_number, kTestServerId);
+  test_api(credit_card_access_manager()).set_is_user_verifiable(true);
+  fido_authenticator().set_is_user_opted_in(true);
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
+  // invoked.
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->risk_based_authentication_invoked());
+  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
+                  ->autofill_progress_dialog_shown());
+
+  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
+  // indicates a yellow path when `fido_request_options` and `context_token` are
+  // returned.
+  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+          .with_result(CreditCardRiskBasedAuthenticator::
+                           RiskBasedAuthenticationResponse::Result::
+                               kAuthenticationRequired)
+          .with_fido_request_options(GetTestRequestOptions())
+          .with_context_token("fake_context_token"));
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.BetterAuth.FlowEvents.Fido",
+      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
+
+  // Expect the CreditCardAccessManager invokes the FIDO authenticator.
+  ASSERT_TRUE(fido_authenticator().authenticate_invoked());
+  EXPECT_EQ(fido_authenticator().card().number(),
+            base::UTF8ToUTF16(test_number));
+  EXPECT_EQ(fido_authenticator().card().record_type(),
+            CreditCard::RecordType::kMaskedServerCard);
+  ASSERT_TRUE(fido_authenticator().context_token().has_value());
+  EXPECT_EQ(fido_authenticator().context_token().value(), "fake_context_token");
+
+  // Expect that we did not signal that there was no interactive authentication.
+  EXPECT_FALSE(
+      test_api(*autofill_client_.GetFormDataImporter())
+          .payment_method_type_if_non_interactive_authentication_flow_completed()
+          .has_value());
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.BetterAuth.CardUnmaskTypeDecision",
+      autofill_metrics::CardUnmaskTypeDecisionMetric::kFidoOnly, 1);
+}
+
+// Ensures that use of new card invokes authorization flow when user is
+// opted-in to FIDO.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_CvcThenFido) {
+  base::HistogramTester histogram_tester;
+
+  OptUserInToFido();
+  std::string test_number = "4444333322221111";
+  CreditCard* masked_server_card =
+      CreateServerCard(kTestGUID, test_number, kTestServerId);
+
+  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
+  payments_network_interface().SetFidoRequestOptionsInUnmaskDetails(
+      kCredentialId, kGooglePaymentsRpid);
+  credit_card_access_manager().PrepareToFetchCreditCard();
+  WaitForCallbacks();
+
+  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.BetterAuth.FlowEvents.CvcThenFido",
+      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
+
+  // Expect CVC prompt to be invoked.
+  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, test_number,
+                                   TestFidoRequestOptionsType::kNotPresent));
+  // Ensure that the form is not filled yet (OnCreditCardFetched is not called).
+  EXPECT_EQ(accessor_->number(), std::u16string());
+  EXPECT_EQ(accessor_->cvc(), std::u16string());
+
+  // Mock user response.
+  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW,
+            GetFIDOAuthenticator()->current_flow());
+  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
+                                                /*did_succeed=*/true);
+  // Ensure that the form is filled after user verification (OnCreditCardFetched
+  // is called).
+  EXPECT_EQ(base::UTF8ToUTF16(test_number), accessor_->number());
+  EXPECT_EQ(kTestCvc16, accessor_->cvc());
+
+  // Mock OptChange payments call.
+  OptChange(PaymentsRpcResult::kSuccess, true);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.BetterAuth.CardUnmaskTypeDecision",
+      autofill_metrics::CardUnmaskTypeDecisionMetric::kCvcThenFido, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.BetterAuth.WebauthnResult.AuthenticationAfterCVC",
+      autofill_metrics::WebauthnResultMetric::kSuccess, 1);
+  histogram_tester.ExpectBucketCount(
+      "Autofill.BetterAuth.FlowEvents.CvcThenFido",
+      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
+}
+
+// Ensures that the kCvc instead of kCvcThenFido flow is invoked if
+// GetUnmaskDetails preflight call is not finished.
+TEST_F(
+    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+    RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_PreflightCallNotFinished) {
+  base::HistogramTester histogram_tester;
+
+  OptUserInToFido();
+  std::string test_number = "4444333322221111";
+  CreditCard* masked_server_card =
+      CreateServerCard(kTestGUID, test_number, kTestServerId);
+
+  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);
+  credit_card_access_manager().PrepareToFetchCreditCard();
+
+  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card);
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.BetterAuth.FlowEvents.Cvc",
+      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
+
+  // Expect CVC prompt to be invoked.
+  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, test_number));
+  // Ensure that the form is filled.
+  EXPECT_EQ(base::UTF8ToUTF16(test_number), accessor_->number());
+  EXPECT_EQ(kTestCvc16, accessor_->cvc());
+
+  // Expect that we did not signal that there was no interactive authentication.
+  EXPECT_FALSE(
+      test_api(*autofill_client_.GetFormDataImporter())
+          .payment_method_type_if_non_interactive_authentication_flow_completed()
+          .has_value());
+}
+
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
+
+// Ensures that CVC filling gets logged after masked server card risk-based
+// unmasking success if the card has CVC.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       LogCvcFilling_RiskBasedMaskedServerCardUnmaskingSuccess) {
+  base::HistogramTester histogram_tester;
+  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
+  CreditCard* masked_server_card =
+      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
+  masked_server_card->set_cvc(kTestCvc16);
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Mock CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse to
+  // successfully return the valid card number.
+  CreditCard card = *masked_server_card;
+  card.set_record_type(CreditCard::RecordType::kFullServerCard);
+  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
+  // indicates a green path with valid card number returned.
+  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+          .with_result(CreditCardRiskBasedAuthenticator::
+                           RiskBasedAuthenticationResponse::Result::
+                               kNoAuthenticationRequired)
+          .with_card(card));
+
+  // Expect the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcStorage.CvcFilling.ServerCard",
+      autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 1);
+}
+
+// Ensures that CVC filling doesn't get logged after after masked server card
+// risk-based unmasking success if the card doesn't have CVC.
+TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+       DoNotLogCvcFilling_RiskBasedMaskedServerCardUnmaskingSuccess) {
+  base::HistogramTester histogram_tester;
+  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
+  CreditCard* masked_server_card =
+      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
+  masked_server_card->set_cvc(u"");
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Mock CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse to
+  // successfully return the valid card number.
+  CreditCard card = *masked_server_card;
+  card.set_record_type(CreditCard::RecordType::kFullServerCard);
+  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
+  // indicates a green path with valid card number returned.
+  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
+      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
+          .with_result(CreditCardRiskBasedAuthenticator::
+                           RiskBasedAuthenticationResponse::Result::
+                               kNoAuthenticationRequired)
+          .with_card(card));
+
+  // Expect the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.CvcStorage.CvcFilling.ServerCard",
+      autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 0);
+}
+
+// Ensures that the masked server card risk-based authentication is not invoked
+// when the card is expired.
+TEST_F(
+    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+    RiskBasedMaskedServerCardUnmasking_RiskBasedAuthenticationNotInvoked_CardExpired) {
+  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
+  CreditCard* masked_server_card =
+      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
+  masked_server_card->SetExpirationYearFromString(u"2010");
+
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+
+  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is not invoked.
+  ASSERT_FALSE(autofill_client_.GetPaymentsAutofillClient()
+                   ->risk_based_authentication_invoked());
+}
+
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
+// Params of the
+// CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest:
+// -- bool fido_opted_in;
+// -- bool preflight_call_returned;
+class
+    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest
+    : public CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
+      public testing::WithParamInterface<std::tuple<bool, bool>> {
+ public:
+  CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest() =
+      default;
+  ~CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest()
+      override = default;
+
+  bool FidoOptedIn() { return std::get<0>(GetParam()); }
+  bool PreflightCallReturned() { return std::get<1>(GetParam()); }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest,
+    testing::Combine(testing::Bool(), testing::Bool()));
+
+// Ensures that the metric for if the preflight call's response is received
+// before card selection is logged correctly.
+TEST_P(
+    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest,
+    Metrics_LogPreflightCallResponseReceivedOnCardSelection) {
+  base::HistogramTester histogram_tester;
+  std::string test_number = "4444333322221111";
+  CreditCard* masked_server_card = CreateServerCard(kTestGUID, test_number);
+  GetFIDOAuthenticator()->SetUserVerifiable(true);
+  if (FidoOptedIn()) {
+    OptUserInToFido();
+  }
+  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(
+      PreflightCallReturned());
+
+  std::string histogram_name =
+      "Autofill.BetterAuth.PreflightCallResponseReceivedOnCardSelection.";
+  histogram_name += FidoOptedIn() ? "OptedIn." : "OptedOut.";
+  histogram_name += "ServerCard";
+
+  credit_card_access_manager().PrepareToFetchCreditCard();
+  credit_card_access_manager().FetchCreditCard(
+      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                                         accessor_->GetWeakPtr()));
+  WaitForCallbacks();
+
+  histogram_tester.ExpectUniqueSample(
+      histogram_name,
+      PreflightCallReturned() ? autofill_metrics::PreflightCallEvent::
+                                    kPreflightCallReturnedBeforeCardChosen
+                              : autofill_metrics::PreflightCallEvent::
+                                    kCardChosenBeforePreflightCallReturned,
+      1);
+}
+
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
+
+}  // namespace
+}  // namespace autofill
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 6e27a631..7de2e39 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -32,6 +32,7 @@
 #include "components/autofill/core/browser/payments/credit_card_risk_based_authenticator.h"
 #include "components/autofill/core/browser/payments/mandatory_reauth_manager.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
+#include "components/autofill/core/browser/payments/payments_window_manager.h"
 #include "components/autofill/core/browser/payments/test/mock_payments_window_manager.h"
 #include "components/autofill/core/browser/payments/test/test_credit_card_otp_authenticator.h"
 #include "components/autofill/core/browser/payments_data_manager.h"
@@ -2594,532 +2595,6 @@
           kTestGUID2)));
 }
 
-class CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest
-    : public CreditCardAccessManagerTestBase {
- public:
-  CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest() = default;
-  ~CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest() override =
-      default;
-
-  base::test::ScopedFeatureList feature_list_{
-      features::kAutofillEnableFpanRiskBasedAuthentication};
-
-  void MockRiskBasedAuthSucceedsWithoutPanReturned(
-      CreditCard* card,
-      std::string context_token = "fake_context_token") {
-    credit_card_access_manager().FetchCreditCard(
-        card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                             accessor_->GetWeakPtr()));
-
-    // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
-    // invoked.
-    EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                    ->risk_based_authentication_invoked());
-    EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                    ->autofill_progress_dialog_shown());
-
-    // Mock that
-    // CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
-    // indicates a yellow path with context token returned.
-    credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
-        CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
-            .with_result(CreditCardRiskBasedAuthenticator::
-                             RiskBasedAuthenticationResponse::Result::
-                                 kAuthenticationRequired)
-            .with_context_token(context_token));
-  }
-};
-
-// Test the flow when the masked server card is successfully returned from
-// the server during a risk-based retrieval.
-TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-       RiskBasedMaskedServerCardUnmasking_Success) {
-#if BUILDFLAG(IS_ANDROID)
-  if (base::android::BuildInfo::GetInstance()->is_automotive()) {
-    GTEST_SKIP() << "This test should not run on automotive.";
-  }
-#endif  // BUILDFLAG(IS_ANDROID)
-
-  base::HistogramTester histogram_tester;
-  std::string test_number = "4444333322221111";
-  CreditCard* masked_server_card =
-      CreateServerCard(kTestGUID, test_number, kTestServerId);
-
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-
-  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
-  // invoked.
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->risk_based_authentication_invoked());
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->autofill_progress_dialog_shown());
-
-  CreditCard card = *masked_server_card;
-  card.set_record_type(CreditCard::RecordType::kFullServerCard);
-  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
-  // indicates a green path with valid card number returned.
-  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
-      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
-          .with_result(CreditCardRiskBasedAuthenticator::
-                           RiskBasedAuthenticationResponse::Result::
-                               kNoAuthenticationRequired)
-          .with_card(card));
-
-  // Ensure the accessor received the correct response.
-  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
-  EXPECT_EQ(accessor_->number(), base::UTF8ToUTF16(test_number));
-
-  // There was no interactive authentication in this flow, so check that this
-  // is signaled correctly.
-  std::optional<NonInteractivePaymentMethodType> type =
-      test_api(*autofill_client_.GetFormDataImporter())
-          .payment_method_type_if_non_interactive_authentication_flow_completed();
-  EXPECT_THAT(type, testing::Optional(
-                        NonInteractivePaymentMethodType::kMaskedServerCard));
-
-  // Expect the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.ServerCardUnmask.ServerCard.Result.RiskBased",
-      autofill_metrics::ServerCardUnmaskResult::kRiskBasedUnmasked, 1);
-}
-
-// Ensures that the masked server card risk-based unmasking response is
-// handled correctly if the retrieval failed.
-TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-       RiskBasedMaskedServerCardUnmasking_RetrievalError) {
-  base::HistogramTester histogram_tester;
-  CreditCard* masked_server_card =
-      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
-
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-
-  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
-  // invoked.
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->risk_based_authentication_invoked());
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->autofill_progress_dialog_shown());
-
-  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
-  // indicates a red path.
-  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
-      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
-          .with_result(CreditCardRiskBasedAuthenticator::
-                           RiskBasedAuthenticationResponse::Result::kError));
-
-  // Expect the CreditCardAccessManager to end the session.
-  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->autofill_error_dialog_shown());
-
-  // Expect the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.ServerCardUnmask.ServerCard.Result.RiskBased",
-      autofill_metrics::ServerCardUnmaskResult::kUnexpectedError, 1);
-}
-
-// Ensures that the masked server card risk-based unmasking response is
-// handled correctly if the flow is cancelled.
-TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-       RiskBasedMaskedServerCardUnmasking_FlowCancelled) {
-  base::HistogramTester histogram_tester;
-  CreditCard* masked_server_card =
-      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
-
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-
-  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
-  // invoked.
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->risk_based_authentication_invoked());
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->autofill_progress_dialog_shown());
-
-  // Mock the authentication is cancelled.
-  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
-      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
-          .with_result(CreditCardRiskBasedAuthenticator::
-                           RiskBasedAuthenticationResponse::Result::
-                               kAuthenticationCancelled));
-
-  // Expect the CreditCardAccessManager to end the session.
-  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
-
-  // Expect the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.ServerCardUnmask.ServerCard.Result.RiskBased",
-      autofill_metrics::ServerCardUnmaskResult::kFlowCancelled, 1);
-}
-
-// Ensures that the masked server card risk-based authentication is not invoked
-// when the feature is disabled.
-TEST_F(
-    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-    RiskBasedMaskedServerCardUnmasking_RiskBasedAuthenticationNotInvoked_FeatureDisabled) {
-  feature_list_.Reset();
-  feature_list_.InitAndDisableFeature(
-      features::kAutofillEnableFpanRiskBasedAuthentication);
-  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
-  CreditCard* masked_server_card =
-      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
-
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-
-  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is not invoked.
-  ASSERT_FALSE(autofill_client_.GetPaymentsAutofillClient()
-                   ->risk_based_authentication_invoked());
-}
-
-TEST_F(
-    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-    RiskBasedMaskedServerCardUnmasking_CvcAuthenticationRequired_ContextTokenSetCorrectly) {
-  std::string context_token = "context_token";
-  CreditCard* masked_server_card =
-      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
-
-  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card,
-                                              context_token);
-
-  // Expect the context_token is set in the full card request.
-  EXPECT_EQ(GetCvcAuthenticator()
-                .GetFullCardRequest()
-                ->GetUnmaskRequestDetailsForTesting()
-                ->context_token,
-            context_token);
-}
-
-// Ensures the authentication is delegated to the CVC authenticator when
-// `fido_request_options` is not returned.
-TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_CvcOnly) {
-  base::HistogramTester histogram_tester;
-
-  std::string test_number = "4444333322221111";
-  CreditCard* masked_server_card =
-      CreateServerCard(kTestGUID, test_number, kTestServerId);
-
-  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card);
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.BetterAuth.FlowEvents.Cvc",
-      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
-
-  // Expect CVC prompt to be invoked.
-  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, test_number));
-  // Ensure that the form is filled.
-  EXPECT_EQ(base::UTF8ToUTF16(test_number), accessor_->number());
-  EXPECT_EQ(kTestCvc16, accessor_->cvc());
-
-  // Expect that we did not signal that there was no interactive authentication.
-  EXPECT_FALSE(
-      test_api(*autofill_client_.GetFormDataImporter())
-          .payment_method_type_if_non_interactive_authentication_flow_completed()
-          .has_value());
-}
-
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
-// Ensures the masked server card risk-based unmasking response is handled
-// correctly and authentication is delegated to the FIDO authenticator, when
-// `fido_request_options` is returned.
-TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_FidoOnly) {
-  base::HistogramTester histogram_tester;
-
-  std::string test_number = "4444333322221111";
-  CreditCard* masked_server_card =
-      CreateServerCard(kTestGUID, test_number, kTestServerId);
-  test_api(credit_card_access_manager()).set_is_user_verifiable(true);
-  fido_authenticator().set_is_user_opted_in(true);
-
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-
-  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
-  // invoked.
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->risk_based_authentication_invoked());
-  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
-                  ->autofill_progress_dialog_shown());
-
-  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
-  // indicates a yellow path when `fido_request_options` and `context_token` are
-  // returned.
-  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
-      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
-          .with_result(CreditCardRiskBasedAuthenticator::
-                           RiskBasedAuthenticationResponse::Result::
-                               kAuthenticationRequired)
-          .with_fido_request_options(GetTestRequestOptions())
-          .with_context_token("fake_context_token"));
-
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.BetterAuth.FlowEvents.Fido",
-      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
-
-  // Expect the CreditCardAccessManager invokes the FIDO authenticator.
-  ASSERT_TRUE(fido_authenticator().authenticate_invoked());
-  EXPECT_EQ(fido_authenticator().card().number(),
-            base::UTF8ToUTF16(test_number));
-  EXPECT_EQ(fido_authenticator().card().record_type(),
-            CreditCard::RecordType::kMaskedServerCard);
-  ASSERT_TRUE(fido_authenticator().context_token().has_value());
-  EXPECT_EQ(fido_authenticator().context_token().value(), "fake_context_token");
-
-  // Expect that we did not signal that there was no interactive authentication.
-  EXPECT_FALSE(
-      test_api(*autofill_client_.GetFormDataImporter())
-          .payment_method_type_if_non_interactive_authentication_flow_completed()
-          .has_value());
-
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.BetterAuth.CardUnmaskTypeDecision",
-      autofill_metrics::CardUnmaskTypeDecisionMetric::kFidoOnly, 1);
-}
-
-// Ensures that use of new card invokes authorization flow when user is
-// opted-in to FIDO.
-TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_CvcThenFido) {
-  base::HistogramTester histogram_tester;
-
-  OptUserInToFido();
-  std::string test_number = "4444333322221111";
-  CreditCard* masked_server_card =
-      CreateServerCard(kTestGUID, test_number, kTestServerId);
-
-  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
-  payments_network_interface().SetFidoRequestOptionsInUnmaskDetails(
-      kCredentialId, kGooglePaymentsRpid);
-  credit_card_access_manager().PrepareToFetchCreditCard();
-  WaitForCallbacks();
-
-  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card);
-
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.BetterAuth.FlowEvents.CvcThenFido",
-      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
-
-  // Expect CVC prompt to be invoked.
-  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, test_number,
-                                   TestFidoRequestOptionsType::kNotPresent));
-  // Ensure that the form is not filled yet (OnCreditCardFetched is not called).
-  EXPECT_EQ(accessor_->number(), std::u16string());
-  EXPECT_EQ(accessor_->cvc(), std::u16string());
-
-  // Mock user response.
-  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW,
-            GetFIDOAuthenticator()->current_flow());
-  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
-                                                /*did_succeed=*/true);
-  // Ensure that the form is filled after user verification (OnCreditCardFetched
-  // is called).
-  EXPECT_EQ(base::UTF8ToUTF16(test_number), accessor_->number());
-  EXPECT_EQ(kTestCvc16, accessor_->cvc());
-
-  // Mock OptChange payments call.
-  OptChange(PaymentsRpcResult::kSuccess, true);
-
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.BetterAuth.CardUnmaskTypeDecision",
-      autofill_metrics::CardUnmaskTypeDecisionMetric::kCvcThenFido, 1);
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.BetterAuth.WebauthnResult.AuthenticationAfterCVC",
-      autofill_metrics::WebauthnResultMetric::kSuccess, 1);
-  histogram_tester.ExpectBucketCount(
-      "Autofill.BetterAuth.FlowEvents.CvcThenFido",
-      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
-}
-
-// Ensures that the kCvc instead of kCvcThenFido flow is invoked if
-// GetUnmaskDetails preflight call is not finished.
-TEST_F(
-    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-    RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_PreflightCallNotFinished) {
-  base::HistogramTester histogram_tester;
-
-  OptUserInToFido();
-  std::string test_number = "4444333322221111";
-  CreditCard* masked_server_card =
-      CreateServerCard(kTestGUID, test_number, kTestServerId);
-
-  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);
-  credit_card_access_manager().PrepareToFetchCreditCard();
-
-  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card);
-
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.BetterAuth.FlowEvents.Cvc",
-      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
-
-  // Expect CVC prompt to be invoked.
-  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, test_number));
-  // Ensure that the form is filled.
-  EXPECT_EQ(base::UTF8ToUTF16(test_number), accessor_->number());
-  EXPECT_EQ(kTestCvc16, accessor_->cvc());
-
-  // Expect that we did not signal that there was no interactive authentication.
-  EXPECT_FALSE(
-      test_api(*autofill_client_.GetFormDataImporter())
-          .payment_method_type_if_non_interactive_authentication_flow_completed()
-          .has_value());
-}
-
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
-
-// Ensures that CVC filling gets logged after masked server card risk-based
-// unmasking success if the card has CVC.
-TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-       LogCvcFilling_RiskBasedMaskedServerCardUnmaskingSuccess) {
-  base::HistogramTester histogram_tester;
-  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
-  CreditCard* masked_server_card =
-      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
-  masked_server_card->set_cvc(kTestCvc16);
-
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-
-  // Mock CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse to
-  // successfully return the valid card number.
-  CreditCard card = *masked_server_card;
-  card.set_record_type(CreditCard::RecordType::kFullServerCard);
-  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
-  // indicates a green path with valid card number returned.
-  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
-      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
-          .with_result(CreditCardRiskBasedAuthenticator::
-                           RiskBasedAuthenticationResponse::Result::
-                               kNoAuthenticationRequired)
-          .with_card(card));
-
-  // Expect the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.CvcStorage.CvcFilling.ServerCard",
-      autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 1);
-}
-
-// Ensures that CVC filling doesn't get logged after after masked server card
-// risk-based unmasking success if the card doesn't have CVC.
-TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-       DoNotLogCvcFilling_RiskBasedMaskedServerCardUnmaskingSuccess) {
-  base::HistogramTester histogram_tester;
-  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
-  CreditCard* masked_server_card =
-      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
-  masked_server_card->set_cvc(u"");
-
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-
-  // Mock CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse to
-  // successfully return the valid card number.
-  CreditCard card = *masked_server_card;
-  card.set_record_type(CreditCard::RecordType::kFullServerCard);
-  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
-  // indicates a green path with valid card number returned.
-  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
-      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
-          .with_result(CreditCardRiskBasedAuthenticator::
-                           RiskBasedAuthenticationResponse::Result::
-                               kNoAuthenticationRequired)
-          .with_card(card));
-
-  // Expect the metrics are logged correctly.
-  histogram_tester.ExpectUniqueSample(
-      "Autofill.CvcStorage.CvcFilling.ServerCard",
-      autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 0);
-}
-
-// Ensures that the masked server card risk-based authentication is not invoked
-// when the card is expired.
-TEST_F(
-    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-    RiskBasedMaskedServerCardUnmasking_RiskBasedAuthenticationNotInvoked_CardExpired) {
-  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
-  CreditCard* masked_server_card =
-      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
-  masked_server_card->SetExpirationYearFromString(u"2010");
-
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-
-  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is not invoked.
-  ASSERT_FALSE(autofill_client_.GetPaymentsAutofillClient()
-                   ->risk_based_authentication_invoked());
-}
-
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
-// Params of the
-// CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest:
-// -- bool fido_opted_in;
-// -- bool preflight_call_returned;
-class
-    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest
-    : public CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
-      public testing::WithParamInterface<std::tuple<bool, bool>> {
- public:
-  CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest() =
-      default;
-  ~CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest()
-      override = default;
-
-  bool FidoOptedIn() { return std::get<0>(GetParam()); }
-  bool PreflightCallReturned() { return std::get<1>(GetParam()); }
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest,
-    testing::Combine(testing::Bool(), testing::Bool()));
-
-// Ensures that the metric for if the preflight call's response is received
-// before card selection is logged correctly.
-TEST_P(
-    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest,
-    Metrics_LogPreflightCallResponseReceivedOnCardSelection) {
-  base::HistogramTester histogram_tester;
-  std::string test_number = "4444333322221111";
-  CreditCard* masked_server_card = CreateServerCard(kTestGUID, test_number);
-  GetFIDOAuthenticator()->SetUserVerifiable(true);
-  if (FidoOptedIn()) {
-    OptUserInToFido();
-  }
-  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(
-      PreflightCallReturned());
-
-  std::string histogram_name =
-      "Autofill.BetterAuth.PreflightCallResponseReceivedOnCardSelection.";
-  histogram_name += FidoOptedIn() ? "OptedIn." : "OptedOut.";
-  histogram_name += "ServerCard";
-
-  credit_card_access_manager().PrepareToFetchCreditCard();
-  credit_card_access_manager().FetchCreditCard(
-      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
-                                         accessor_->GetWeakPtr()));
-  WaitForCallbacks();
-
-  histogram_tester.ExpectUniqueSample(
-      histogram_name,
-      PreflightCallReturned() ? autofill_metrics::PreflightCallEvent::
-                                    kPreflightCallReturnedBeforeCardChosen
-                              : autofill_metrics::PreflightCallEvent::
-                                    kCardChosenBeforePreflightCallReturned,
-      1);
-}
-
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
-
 TEST_F(CreditCardAccessManagerTest, IsVirtualCardPresentInUnmaskedCache) {
   CreditCard* masked_server_card =
       CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
@@ -3484,16 +2959,20 @@
 }
 
 // Test that a success response for a VCN 3DS authentication is handled
-// correctly and notifies the caller with the proper fields set.
+// correctly and notifies the caller with the proper fields set, and the
+// authentication unmasked server card result metric bucket is logged to.
 TEST_F(CreditCardAccessManagerTest,
        VirtualCardUnmasking_3dsResponseReceived_Success) {
   // Set up the test.
+  base::HistogramTester histogram_tester;
   CreditCard card = test::GetVirtualCard();
   credit_card_access_manager().FetchCreditCard(
       &card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                             accessor_->GetWeakPtr()));
   payments::PaymentsWindowManager::Vcn3dsAuthenticationResponse response;
   response.card = card;
+  response.result =
+      payments::PaymentsWindowManager::Vcn3dsAuthenticationResult::kSuccess;
 
   // Mock the VCN 3DS authentication response.
   test_api(credit_card_access_manager())
@@ -3505,18 +2984,25 @@
   EXPECT_EQ(accessor_->cvc(), response.card->cvc());
   EXPECT_FALSE(
       test_api(credit_card_access_manager()).is_authentication_in_progress());
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ServerCardUnmask.VirtualCard.Result.ThreeDomainSecure",
+      autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
 }
 
 // Test that a failure response for a VCN 3DS authentication is handled
-// correctly and notifies the caller with the proper fields set.
+// correctly and notifies the caller with the proper fields set, and the virtual
+// card retrieval error server card unmask result metric bucket is logged to.
 TEST_F(CreditCardAccessManagerTest,
-       VirtualCardUnmasking_3dsResponseReceived_Error) {
+       VirtualCardUnmasking_3dsResponseReceived_AuthenticationError) {
   // Set up the test.
+  base::HistogramTester histogram_tester;
   CreditCard card = test::GetVirtualCard();
   credit_card_access_manager().FetchCreditCard(
       &card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                             accessor_->GetWeakPtr()));
   payments::PaymentsWindowManager::Vcn3dsAuthenticationResponse response;
+  response.result = payments::PaymentsWindowManager::
+      Vcn3dsAuthenticationResult::kAuthenticationFailed;
 
   // Mock the VCN 3DS authentication response.
   test_api(credit_card_access_manager())
@@ -3526,6 +3012,36 @@
   EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
   EXPECT_FALSE(
       test_api(credit_card_access_manager()).is_authentication_in_progress());
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ServerCardUnmask.VirtualCard.Result.ThreeDomainSecure",
+      autofill_metrics::ServerCardUnmaskResult::kVirtualCardRetrievalError, 1);
+}
+
+// Test that the user cancelling a VCN 3DS authentication is handled correctly
+// and notifies the caller with the proper fields set, and the flow cancelled
+// result metric bucket is logged to.
+TEST_F(
+    CreditCardAccessManagerTest,
+    VirtualCardUnmasking_3dsResponseReceived_AuthenticationNotCompletedError) {
+  // Set up the test.
+  base::HistogramTester histogram_tester;
+  CreditCard card = test::GetVirtualCard();
+  credit_card_access_manager().FetchCreditCard(
+      &card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
+                            accessor_->GetWeakPtr()));
+  payments::PaymentsWindowManager::Vcn3dsAuthenticationResponse response;
+  response.result = payments::PaymentsWindowManager::
+      Vcn3dsAuthenticationResult::kAuthenticationNotCompleted;
+
+  // Mock the VCN 3DS authentication response.
+  test_api(credit_card_access_manager())
+      .OnVcn3dsAuthenticationComplete(response);
+
+  // Check that `accessor_` was triggered with the expected error.
+  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ServerCardUnmask.VirtualCard.Result.ThreeDomainSecure",
+      autofill_metrics::ServerCardUnmaskResult::kFlowCancelled, 1);
 }
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
diff --git a/components/autofill/core/browser/payments/payments_window_manager.h b/components/autofill/core/browser/payments/payments_window_manager.h
index edc59b5f..d508967 100644
--- a/components/autofill/core/browser/payments/payments_window_manager.h
+++ b/components/autofill/core/browser/payments/payments_window_manager.h
@@ -23,8 +23,26 @@
   using RedirectCompletionResult =
       base::StrongAlias<class RedirectCompletionResultTag, std::string>;
 
-  // The response fields for a VCN 3DS authentication, created once a response
-  // to the second UnmaskCardRequest has been received.
+  // The result of the VCN 3DS authentication.
+  enum class Vcn3dsAuthenticationResult {
+    // The authentication was a success.
+    kSuccess = 0,
+    // The authentication was a failure. If the authentication failed inside of
+    // the pop-up, the reason for the failure is unknown to Chrome, and can be
+    // due to any of several possible reasons. Some reasons can be that the user
+    // failed to authenticate, or there is a server error. This can also mean
+    // the authentication failed during the Payments server call to retrieve the
+    // card after the pop-up has closed.
+    kAuthenticationFailed = 1,
+    // The authentication did not complete. This occurs if the user closes the
+    // pop-up before finishing the authentication, and there are no query
+    // params. This can also occur if the user cancels any of the dialogs during
+    // the flow.
+    kAuthenticationNotCompleted = 2,
+  };
+
+  // The response fields for a VCN 3DS authentication, created once the flow is
+  // complete and a response to the caller is required.
   struct Vcn3dsAuthenticationResponse {
     Vcn3dsAuthenticationResponse();
     Vcn3dsAuthenticationResponse(const Vcn3dsAuthenticationResponse&);
@@ -34,9 +52,12 @@
     Vcn3dsAuthenticationResponse& operator=(Vcn3dsAuthenticationResponse&&);
     ~Vcn3dsAuthenticationResponse();
 
+    // The result of the VCN 3DS authentication.
+    Vcn3dsAuthenticationResult result;
+
     // CreditCard representation of the data returned in the response of the
-    // UnmaskCardRequest after a VCN 3DS authentication has completed. The
-    // response is a success if `card` is present, it is a failure otherwise.
+    // UnmaskCardRequest after a VCN 3DS authentication has completed. Only
+    // present if `result` is a success.
     std::optional<CreditCard> card;
   };
 
@@ -70,20 +91,6 @@
     bool user_consent_already_given = false;
   };
 
-  // The result of the 3DS authentication inside of the pop-up if it was not a
-  // success.
-  enum class Vcn3dsAuthenticationPopupNonSuccessResult {
-    // The authentication inside of the 3DS pop-up was a failure. The reason for
-    // the failure is unknown to Chrome, and can be due to any of several
-    // possible reasons. Some reasons can be that the user failed to
-    // authenticate, or there is a server error.
-    kAuthenticationFailed = 0,
-    // The authentication inside of the 3DS pop-up did not complete. This occurs
-    // if the user closes the pop-up before finishing the authentication, and
-    // there are no query params.
-    kAuthenticationNotCompleted = 1,
-  };
-
   virtual ~PaymentsWindowManager() = default;
 
   // Initiates the VCN 3DS auth flow. All fields in `context` must be valid and
diff --git a/components/autofill/core/browser/payments/payments_window_manager_util.cc b/components/autofill/core/browser/payments/payments_window_manager_util.cc
index 3e444def..d1aacc3 100644
--- a/components/autofill/core/browser/payments/payments_window_manager_util.cc
+++ b/components/autofill/core/browser/payments/payments_window_manager_util.cc
@@ -17,7 +17,7 @@
 namespace autofill::payments {
 
 base::expected<PaymentsWindowManager::RedirectCompletionResult,
-               PaymentsWindowManager::Vcn3dsAuthenticationPopupNonSuccessResult>
+               PaymentsWindowManager::Vcn3dsAuthenticationResult>
 ParseUrlForVcn3ds(const GURL& url,
                   const Vcn3dsChallengeOptionMetadata& metadata) {
   std::string redirect_completion_result;
@@ -46,14 +46,12 @@
 
   // `is_failure` being true indicates the authentication has failed.
   if (is_failure) {
-    return base::unexpected(
-        PaymentsWindowManager::Vcn3dsAuthenticationPopupNonSuccessResult::
-            kAuthenticationFailed);
+    return base::unexpected(PaymentsWindowManager::Vcn3dsAuthenticationResult::
+                                kAuthenticationFailed);
   }
 
-  return base::unexpected(
-      PaymentsWindowManager::Vcn3dsAuthenticationPopupNonSuccessResult::
-          kAuthenticationNotCompleted);
+  return base::unexpected(PaymentsWindowManager::Vcn3dsAuthenticationResult::
+                              kAuthenticationNotCompleted);
 }
 
 PaymentsNetworkInterface::UnmaskRequestDetails
@@ -87,12 +85,14 @@
 }
 
 PaymentsWindowManager::Vcn3dsAuthenticationResponse
-CreateVcn3dsAuthenticationResponse(
+CreateVcn3dsAuthenticationResponseFromServerResult(
     PaymentsAutofillClient::PaymentsRpcResult result,
     const PaymentsNetworkInterface::UnmaskResponseDetails& response_details,
     CreditCard card) {
   PaymentsWindowManager::Vcn3dsAuthenticationResponse response;
   if (result == PaymentsAutofillClient::PaymentsRpcResult::kSuccess) {
+    response.result =
+        PaymentsWindowManager::Vcn3dsAuthenticationResult::kSuccess;
     card.SetNumber(base::UTF8ToUTF16(response_details.real_pan));
     card.SetExpirationMonthFromString(
         base::UTF8ToUTF16(response_details.expiration_month),
@@ -101,6 +101,9 @@
         base::UTF8ToUTF16(response_details.expiration_year));
     card.set_cvc(base::UTF8ToUTF16(response_details.dcvv));
     response.card = std::move(card);
+  } else {
+    response.result = PaymentsWindowManager::Vcn3dsAuthenticationResult::
+        kAuthenticationFailed;
   }
   return response;
 }
diff --git a/components/autofill/core/browser/payments/payments_window_manager_util.h b/components/autofill/core/browser/payments/payments_window_manager_util.h
index 646047f..bd74ae3 100644
--- a/components/autofill/core/browser/payments/payments_window_manager_util.h
+++ b/components/autofill/core/browser/payments/payments_window_manager_util.h
@@ -25,7 +25,7 @@
 // return a PaymentsWindowManager::RedirectCompletionResult as the expected
 // response. Otherwise this function will return the non-success result.
 base::expected<PaymentsWindowManager::RedirectCompletionResult,
-               PaymentsWindowManager::Vcn3dsAuthenticationPopupNonSuccessResult>
+               PaymentsWindowManager::Vcn3dsAuthenticationResult>
 ParseUrlForVcn3ds(const GURL& url,
                   const Vcn3dsChallengeOptionMetadata& metadata);
 
@@ -44,7 +44,7 @@
 // Creates the Vcn3dsAuthenticationResponse for the response from the
 // UnmaskCardRequest that was sent during the VCN 3DS authentication.
 PaymentsWindowManager::Vcn3dsAuthenticationResponse
-CreateVcn3dsAuthenticationResponse(
+CreateVcn3dsAuthenticationResponseFromServerResult(
     PaymentsAutofillClient::PaymentsRpcResult result,
     const PaymentsNetworkInterface::UnmaskResponseDetails& response_details,
     CreditCard card);
diff --git a/components/autofill/core/browser/payments_data_manager.cc b/components/autofill/core/browser/payments_data_manager.cc
index cecfeb96..a489713 100644
--- a/components/autofill/core/browser/payments_data_manager.cc
+++ b/components/autofill/core/browser/payments_data_manager.cc
@@ -795,8 +795,10 @@
   if (cached_image) {
     return cached_image;
   }
-
-  FetchImagesForURLs(base::span_from_ref(card_art_url));
+  // The sizes are used on Android, but ignored on desktop.
+  FetchImagesForURLs(base::span_from_ref(card_art_url),
+                     base::span({AutofillImageFetcherBase::ImageSize::kSmall,
+                                 AutofillImageFetcherBase::ImageSize::kLarge}));
   return nullptr;
 }
 
@@ -1810,14 +1812,16 @@
 }
 
 void PaymentsDataManager::FetchImagesForURLs(
-    base::span<const GURL> updated_urls) const {
+    base::span<const GURL> updated_urls,
+    base::span<const AutofillImageFetcherBase::ImageSize> image_sizes) const {
   if (!image_fetcher_) {
     return;
   }
 
   image_fetcher_->FetchImagesForURLs(
-      updated_urls, base::BindOnce(&PaymentsDataManager::OnCardArtImagesFetched,
-                                   weak_factory_.GetMutableWeakPtr()));
+      updated_urls, image_sizes,
+      base::BindOnce(&PaymentsDataManager::OnCardArtImagesFetched,
+                     weak_factory_.GetMutableWeakPtr()));
 }
 
 void PaymentsDataManager::LogStoredPaymentsDataMetrics() const {
@@ -1947,7 +1951,10 @@
     }
   }
   if (!updated_urls.empty()) {
-    FetchImagesForURLs(updated_urls);
+    FetchImagesForURLs(
+        updated_urls,
+        base::span({AutofillImageFetcherBase::ImageSize::kSmall,
+                    AutofillImageFetcherBase::ImageSize::kLarge}));
   }
 }
 
@@ -2001,7 +2008,9 @@
     updated_urls.emplace_back(display_icon_url);
   }
   if (!updated_urls.empty()) {
-    FetchImagesForURLs(updated_urls);
+    FetchImagesForURLs(
+        updated_urls,
+        base::span({AutofillImageFetcherBase::ImageSize::kSquare}));
   }
 }
 
diff --git a/components/autofill/core/browser/payments_data_manager.h b/components/autofill/core/browser/payments_data_manager.h
index 2ea2f32..1ff142a9 100644
--- a/components/autofill/core/browser/payments_data_manager.h
+++ b/components/autofill/core/browser/payments_data_manager.h
@@ -27,6 +27,7 @@
 #include "components/autofill/core/browser/metrics/autofill_metrics.h"
 #include "components/autofill/core/browser/payments/account_info_getter.h"
 #include "components/autofill/core/browser/payments/payments_customer_data.h"
+#include "components/autofill/core/browser/ui/autofill_image_fetcher_base.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -43,7 +44,6 @@
 
 namespace autofill {
 
-class AutofillImageFetcherBase;
 class AutofillOptimizationGuide;
 class BankAccount;
 struct CreditCardArtImage;
@@ -517,8 +517,13 @@
   // to the query handle.
   void CancelPendingServerQuery(WebDataServiceBase::Handle* handle);
 
-  // Asks `image_fetcher_` to fetch images.
-  void FetchImagesForURLs(base::span<const GURL> updated_urls) const;
+  // Asks `image_fetcher_` to fetch images. Each image represented by an url in
+  // the list `updated_urls` is downloaded in all the sizes specified by
+  // `image_sizes`. The total # of images downloaded is `updated_urls`.size() x
+  // `image_sizes`.size().
+  void FetchImagesForURLs(
+      base::span<const GURL> updated_urls,
+      base::span<const AutofillImageFetcherBase::ImageSize> image_sizes) const;
 
   // The first time this is called, logs a UMA metrics about the user's credit
   // card, offer and IBAN.
diff --git a/components/autofill/core/browser/payments_data_manager_unittest.cc b/components/autofill/core/browser/payments_data_manager_unittest.cc
index a652c0e..f02aad73 100644
--- a/components/autofill/core/browser/payments_data_manager_unittest.cc
+++ b/components/autofill/core/browser/payments_data_manager_unittest.cc
@@ -253,6 +253,7 @@
       void,
       FetchImagesForURLs,
       (base::span<const GURL> card_art_urls,
+       base::span<const AutofillImageFetcherBase::ImageSize> image_sizes,
        base::OnceCallback<void(
            const std::vector<std::unique_ptr<CreditCardArtImage>>&)> callback),
       (override));
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher.cc b/components/autofill/core/browser/ui/autofill_image_fetcher.cc
index 68ca7df..b507dc6a 100644
--- a/components/autofill/core/browser/ui/autofill_image_fetcher.cc
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher.cc
@@ -58,7 +58,8 @@
 }  // namespace
 
 void AutofillImageFetcher::FetchImagesForURLs(
-    base::span<const GURL> card_art_urls,
+    base::span<const GURL> image_urls,
+    base::span<const AutofillImageFetcherBase::ImageSize> image_sizes_unused,
     base::OnceCallback<void(
         const std::vector<std::unique_ptr<CreditCardArtImage>>&)> callback) {
   if (!GetImageFetcher()) {
@@ -70,10 +71,10 @@
   // only when all the images are fetched.
   const auto barrier_callback =
       base::BarrierCallback<std::unique_ptr<CreditCardArtImage>>(
-          card_art_urls.size(), std::move(callback));
+          image_urls.size(), std::move(callback));
 
-  for (const auto& card_art_url : card_art_urls) {
-    FetchImageForURL(barrier_callback, card_art_url);
+  for (const auto& image_url : image_urls) {
+    FetchImageForURL(barrier_callback, image_url);
   }
 }
 
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher.h b/components/autofill/core/browser/ui/autofill_image_fetcher.h
index 02bb244..0e7f7a0c 100644
--- a/components/autofill/core/browser/ui/autofill_image_fetcher.h
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher.h
@@ -36,8 +36,12 @@
   virtual ~AutofillImageFetcher() = default;
 
   // AutofillImageFetcherBase:
+  // The image sizes passed in the arguments are unused as this param is only
+  // used for Android. For Desktop, the implementation of this method has
+  // hardcoded image sizes.
   void FetchImagesForURLs(
-      base::span<const GURL> card_art_urls,
+      base::span<const GURL> image_urls,
+      base::span<const AutofillImageFetcherBase::ImageSize> image_sizes_unused,
       base::OnceCallback<void(
           const std::vector<std::unique_ptr<CreditCardArtImage>>&)> callback)
       override;
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher_base.h b/components/autofill/core/browser/ui/autofill_image_fetcher_base.h
index 9419615..5898c4c 100644
--- a/components/autofill/core/browser/ui/autofill_image_fetcher_base.h
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher_base.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_AUTOFILL_IMAGE_FETCHER_BASE_H_
 
 #include <vector>
-
 #include "base/containers/span.h"
 #include "base/functional/callback.h"
 
@@ -36,17 +35,26 @@
 // Android does not need an intermediate class.
 class AutofillImageFetcherBase {
  public:
+  // Different sizes in which we show the credit card / bank account art images.
+  // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.autofill
+  enum class ImageSize {
+    kSmall = 0,
+    kLarge = 1,
+    kSquare = 2,
+  };
   //  TODO (crbug.com/1478931): The implementation classes should own the
   //  fetched images, and define the callback to handle the images.
   //
   // Once invoked, the image fetcher starts fetching images asynchronously based
-  // on the urls. `card_art_urls` is a span of with credit cards' card art image
-  // url. `callback` will be invoked when all the requests have been completed.
-  // The callback will receive a vector of CreditCardArtImage, for (only) those
-  // cards for which the AutofillImageFetcher could successfully fetch the
-  // image.
+  // on the urls. `image_urls` is a span of urls that needs to be downloaded.
+  // `image_sizes` is the different sizes in which each image_url should be
+  // downloaded. `callback` will be invoked when all the requests have been
+  // completed. The callback will receive a vector of CreditCardArtImage, for
+  // (only) those cards for which the AutofillImageFetcher could successfully
+  // fetch the image.
   virtual void FetchImagesForURLs(
-      base::span<const GURL> card_art_urls,
+      base::span<const GURL> image_urls,
+      base::span<const ImageSize> image_sizes,
       base::OnceCallback<
           void(const std::vector<std::unique_ptr<CreditCardArtImage>>&)>
           callback) = 0;
diff --git a/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc b/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc
index c2ff9f8..ffe8a3d 100644
--- a/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc
+++ b/components/autofill/core/browser/ui/autofill_image_fetcher_unittest.cc
@@ -156,7 +156,9 @@
   EXPECT_CALL(*mock_image_fetcher(), FetchImageAndData_(fake_url2, _, _, _))
       .Times(1);
   std::vector<GURL> urls = {fake_url1, fake_url2};
-  autofill_image_fetcher()->FetchImagesForURLs(urls, base::DoNothing());
+  autofill_image_fetcher()->FetchImagesForURLs(
+      urls, base::span({AutofillImageFetcherBase::ImageSize::kSmall}),
+      base::DoNothing());
 
   // Advance the time to make the latency values more realistic.
   task_environment().FastForwardBy(base::Milliseconds(200));
@@ -189,7 +191,9 @@
   EXPECT_CALL(*mock_image_fetcher(), FetchImageAndData_(override_url, _, _, _))
       .Times(1);
   std::vector<GURL> urls = {fake_url1};
-  autofill_image_fetcher()->FetchImagesForURLs(urls, base::DoNothing());
+  autofill_image_fetcher()->FetchImagesForURLs(
+      urls, base::span({AutofillImageFetcherBase::ImageSize::kSmall}),
+      base::DoNothing());
 }
 
 TEST_F(AutofillImageFetcherTest, FetchImage_ResolveCardArtImage) {
@@ -247,7 +251,9 @@
   // Expect to be called once.
   EXPECT_CALL(*mock_image_fetcher(), FetchImageAndData_(_, _, _, _)).Times(1);
   std::vector<GURL> urls = {fake_url1};
-  autofill_image_fetcher()->FetchImagesForURLs(urls, base::DoNothing());
+  autofill_image_fetcher()->FetchImagesForURLs(
+      urls, base::span({AutofillImageFetcherBase::ImageSize::kSmall}),
+      base::DoNothing());
 
   task_environment().FastForwardBy(base::Milliseconds(200));
   // Simulate failed image fetching (for image with URL) -> expect the
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 7a8c379..ce56e0e 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -23,15 +23,6 @@
              "AutofillGivePrecedenceToNumericQuantities",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// TODO(crbug.com/40151750): Remove this feature flag after the explicit save
-// prompts for address profiles is complete.
-// When enabled, address profile save problem will contain a dropdown for
-// assigning a nickname to the address profile. Relevant only if the
-// AutofillAddressProfileSavePrompt feature is enabled.
-BASE_FEATURE(kAutofillAddressProfileSavePromptNicknameSupport,
-             "AutofillAddressProfileSavePromptNicknameSupport",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Feature flag to control the displaying of an ongoing hats survey that
 // measures users perception of Autofill. Differently from other surveys,
 // the Autofill user perception survey will not have a specific target
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 5c7eb8e..24ff5bb1 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -18,8 +18,6 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillGivePrecedenceToNumericQuantities);
 COMPONENT_EXPORT(AUTOFILL)
-BASE_DECLARE_FEATURE(kAutofillAddressProfileSavePromptNicknameSupport);
-COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillAddressUserPerceptionSurvey);
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE(kAutofillCaretExtraction);
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
index 2d64363..1755aec 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.cc
@@ -9,6 +9,7 @@
 #include "base/types/expected.h"
 #include "components/autofill/core/browser/field_type_utils.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/browser/ui/suggestion_type.h"
 #include "components/autofill/core/common/dense_set.h"
@@ -16,6 +17,7 @@
 #include "components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_client.h"
 #include "components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_features.h"
 #include "components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_filling_engine.h"
+#include "components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_utils.h"
 #include "components/optimization_guide/core/optimization_guide_decider.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
 #include "components/optimization_guide/proto/hints.pb.h"
@@ -180,6 +182,15 @@
   return (*cache_).FindFieldByGlobalId(field.global_id());
 }
 
+bool AutofillPredictionImprovementsManager::IsFormEligible(
+    const autofill::FormStructure& form) {
+  if (!IsFormEligibleByFieldCriteria(form)) {
+    return false;
+  }
+
+  return ShouldProvidePredictionImprovements(form.main_frame_origin().GetURL());
+}
+
 bool AutofillPredictionImprovementsManager::MaybeUpdateSuggestions(
     std::vector<autofill::Suggestion>& address_suggestions,
     const autofill::FormFieldData& field,
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.h b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.h
index 3676dcc..ad414a0 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.h
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager.h
@@ -17,6 +17,10 @@
 class OptimizationGuideDecider;
 }
 
+namespace autofill {
+class FormStructure;
+}  // namespace autofill
+
 namespace autofill_prediction_improvements {
 
 // The class for embedder-independent, tab-specific
@@ -38,6 +42,7 @@
       std::vector<autofill::Suggestion>& address_suggestions,
       const autofill::FormFieldData& field,
       bool should_add_trigger_suggestion) override;
+  bool IsFormEligible(const autofill::FormStructure& form) override;
   bool ShouldProvidePredictionImprovements(const GURL& url) override;
   void UserFeedbackReceived(
       autofill::AutofillPredictionImprovementsDelegate::UserFeedback feedback)
diff --git a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
index 2edd915d2..646d36ba 100644
--- a/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
+++ b/components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_manager_unittest.cc
@@ -7,6 +7,8 @@
 #include "base/test/gmock_move_support.h"
 #include "base/test/mock_callback.h"
 #include "components/autofill/core/browser/autofill_form_test_utils.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/form_structure_test_api.h"
 #include "components/autofill/core/common/autofill_test_utils.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill_prediction_improvements/core/browser/autofill_prediction_improvements_features.h"
@@ -410,5 +412,43 @@
                   SuggestionType::kPredictionImprovementsLoadingState)));
 }
 
+TEST_F(ShouldProvideAutofillPredictionImprovementsTest,
+       IsFormEligible_EmptyForm) {
+  feature_.InitAndEnableFeatureWithParameters(kAutofillPredictionImprovements,
+                                              {{"skip_allowlist", "true"}});
+
+  autofill::FormData form_data;
+  autofill::FormStructure form(form_data);
+  autofill::FormStructureTestApi form_test_api(form);
+
+  AutofillPredictionImprovementsManager manager{&client_, &decider_};
+
+  EXPECT_FALSE(manager.IsFormEligible(form));
+}
+
+TEST_F(ShouldProvideAutofillPredictionImprovementsTest,
+       IsFormEligible_EligibleForm) {
+  feature_.InitAndEnableFeatureWithParameters(kAutofillPredictionImprovements,
+                                              {{"skip_allowlist", "true"}});
+
+  autofill::FormData form_data;
+  autofill::FormStructure form(form_data);
+  autofill::FormStructureTestApi form_test_api(form);
+
+  autofill::AutofillField& prediction_improvement_field =
+      form_test_api.PushField();
+#if BUILDFLAG(USE_INTERNAL_AUTOFILL_PATTERNS)
+  prediction_improvement_field.set_heuristic_type(
+      autofill::HeuristicSource::kPredictionImprovementRegexes,
+      autofill::IMPROVED_PREDICTION);
+#else
+  prediction_improvement_field.set_heuristic_type(
+      autofill::HeuristicSource::kLegacyRegexes, autofill::IMPROVED_PREDICTION);
+#endif
+
+  AutofillPredictionImprovementsManager manager{&client_, &decider_};
+
+  EXPECT_TRUE(manager.IsFormEligible(form));
+}
 }  // namespace
 }  // namespace autofill_prediction_improvements
diff --git a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsPage.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsPage.java
index 0d24a7c..f58295d 100644
--- a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsPage.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsPage.java
@@ -6,7 +6,18 @@
 
 import org.chromium.base.supplier.ObservableSupplier;
 
-/** The base interface that all setting page fragments should implement. */
+/**
+ * The base interface that setting page fragments should implement.
+ *
+ * <p>Fragments implementing this interface are called <i>embeddable</i>; otherwise they are
+ * considered <i>standalone</i>.
+ *
+ * <p>Embeddable fragments can be a part of the multi-column UI (if it is enabled). They must not
+ * modify UI outside of the fragment. For example, it should implement {@link getPageTitle} to
+ * provide the page title, instead of modifying the activity title directly.
+ *
+ * <p>Standalone fragments are shown as a whole and has better control of the activity.
+ */
 public interface SettingsPage {
     /**
      * Returns the title of the current setting page.
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd
index 15600e82..8a47f60 100644
--- a/components/browser_ui/strings/android/browser_ui_strings.grd
+++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -1170,6 +1170,12 @@
       <message name="IDS_EDUCATIONAL_TIP_TAB_GROUP_SYNC_DESCRIPTION" desc="Description of the Tab group promo in the educational tip module on NTP.">
         The groups you've created are saved in tab group section and update across your devices
       </message>
+      <message name="IDS_EDUCATIONAL_TIP_QUICK_DELETE_TITLE" desc="Title of the quick delete promo in the educational tip module on NTP.">
+        Manage your browsing data
+      </message>
+      <message name="IDS_EDUCATIONAL_TIP_QUICK_DELETE_DESCRIPTION" desc="Description of the quick delete promo in the educational tip module on NTP.">
+        You can delete some or all of your history, cookies, site data and more
+      </message>
 
       <message name="IDS_GO_TO_OS_SETTINGS" desc="When the user clicks on this text button, Chrome sends them to the device OS Settings. One use is the text button shown below the text 'Get alerts for price drops?', which takes the user to the Android notification settings. Another use is in the primary button of the dialog prompting the user to create a device lock, which takes the user to the Android security settings.">
         Go to Settings
diff --git a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_EDUCATIONAL_TIP_QUICK_DELETE_DESCRIPTION.png.sha1 b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_EDUCATIONAL_TIP_QUICK_DELETE_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..7596d680
--- /dev/null
+++ b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_EDUCATIONAL_TIP_QUICK_DELETE_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+98414c40a7ba84991eefbba78ba01d0acc506972
\ No newline at end of file
diff --git a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_EDUCATIONAL_TIP_QUICK_DELETE_TITLE.png.sha1 b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_EDUCATIONAL_TIP_QUICK_DELETE_TITLE.png.sha1
new file mode 100644
index 0000000..7596d680
--- /dev/null
+++ b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_EDUCATIONAL_TIP_QUICK_DELETE_TITLE.png.sha1
@@ -0,0 +1 @@
+98414c40a7ba84991eefbba78ba01d0acc506972
\ No newline at end of file
diff --git a/components/content_settings/core/browser/content_settings_registry.cc b/components/content_settings/core/browser/content_settings_registry.cc
index 0186b93..6d1fed5 100644
--- a/components/content_settings/core/browser/content_settings_registry.cc
+++ b/components/content_settings/core/browser/content_settings_registry.cc
@@ -478,7 +478,8 @@
            /*valid_settings=*/
            {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_BLOCK},
            WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE,
-           WebsiteSettingsRegistry::DESKTOP,
+           WebsiteSettingsRegistry::DESKTOP |
+               WebsiteSettingsRegistry::PLATFORM_ANDROID,
            ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
            ContentSettingsInfo::EXCEPTIONS_ON_SECURE_ORIGINS_ONLY);
 
@@ -488,7 +489,8 @@
            /*valid_settings=*/
            {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_BLOCK},
            WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE,
-           WebsiteSettingsRegistry::DESKTOP,
+           WebsiteSettingsRegistry::DESKTOP |
+               WebsiteSettingsRegistry::PLATFORM_ANDROID,
            ContentSettingsInfo::INHERIT_IF_LESS_PERMISSIVE,
            ContentSettingsInfo::EXCEPTIONS_ON_SECURE_ORIGINS_ONLY);
 
diff --git a/components/content_settings/core/browser/website_settings_registry.cc b/components/content_settings/core/browser/website_settings_registry.cc
index 05721dd..cdee7a9 100644
--- a/components/content_settings/core/browser/website_settings_registry.cc
+++ b/components/content_settings/core/browser/website_settings_registry.cc
@@ -238,7 +238,8 @@
   Register(ContentSettingsType::FILE_SYSTEM_LAST_PICKED_DIRECTORY,
            "file-system-last-picked-directory", base::Value(),
            WebsiteSettingsInfo::UNSYNCABLE, WebsiteSettingsInfo::NOT_LOSSY,
-           WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE, DESKTOP,
+           WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE,
+           DESKTOP | PLATFORM_ANDROID,
            WebsiteSettingsInfo::DONT_INHERIT_IN_INCOGNITO);
   Register(ContentSettingsType::FEDERATED_IDENTITY_SHARING, "fedcm-share",
            base::Value(), WebsiteSettingsInfo::UNSYNCABLE,
diff --git a/components/content_settings/core/common/content_settings_metadata.cc b/components/content_settings/core/common/content_settings_metadata.cc
index cf09361..b16313d5 100644
--- a/components/content_settings/core/common/content_settings_metadata.cc
+++ b/components/content_settings/core/common/content_settings_metadata.cc
@@ -15,8 +15,15 @@
 namespace content_settings {
 
 RuleMetaData::RuleMetaData() = default;
+
 RuleMetaData::RuleMetaData(const RuleMetaData& other) = default;
 
+RuleMetaData::RuleMetaData(RuleMetaData&& other) = default;
+
+RuleMetaData& RuleMetaData::operator=(const RuleMetaData& other) = default;
+
+RuleMetaData& RuleMetaData::operator=(RuleMetaData&& other) = default;
+
 void RuleMetaData::SetFromConstraints(
     const ContentSettingConstraints& constraints) {
   session_model_ = constraints.session_model();
@@ -37,7 +44,6 @@
   return !expiration().is_null() && expiration() <= clock->Now();
 }
 
-RuleMetaData& RuleMetaData::operator=(const RuleMetaData& other) = default;
 bool RuleMetaData::operator==(const RuleMetaData& other) const = default;
 
 // static
diff --git a/components/content_settings/core/common/content_settings_metadata.h b/components/content_settings/core/common/content_settings_metadata.h
index 70a0ef9e7..36aea92 100644
--- a/components/content_settings/core/common/content_settings_metadata.h
+++ b/components/content_settings/core/common/content_settings_metadata.h
@@ -28,8 +28,10 @@
  public:
   RuleMetaData();
   RuleMetaData(const RuleMetaData& other);
-
+  RuleMetaData(RuleMetaData&& other);
   RuleMetaData& operator=(const RuleMetaData& other);
+  RuleMetaData& operator=(RuleMetaData&& other);
+
   bool operator==(const RuleMetaData& other) const;
 
   base::Time last_modified() const { return last_modified_; }
diff --git a/components/dbus/DEPS b/components/dbus/DEPS
new file mode 100644
index 0000000..4f7b097
--- /dev/null
+++ b/components/dbus/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+dbus",
+]
\ No newline at end of file
diff --git a/components/dbus/thread_linux/BUILD.gn b/components/dbus/thread_linux/BUILD.gn
index a1d14ca29..6a38398 100644
--- a/components/dbus/thread_linux/BUILD.gn
+++ b/components/dbus/thread_linux/BUILD.gn
@@ -11,5 +11,6 @@
   deps = [
     "//base",
     "//build:chromeos_buildflags",
+    "//dbus",
   ]
 }
diff --git a/components/dbus/thread_linux/dbus_thread_linux.h b/components/dbus/thread_linux/dbus_thread_linux.h
index 944a27a..2296622 100644
--- a/components/dbus/thread_linux/dbus_thread_linux.h
+++ b/components/dbus/thread_linux/dbus_thread_linux.h
@@ -13,7 +13,7 @@
 
 // Many APIs in ::dbus are required to be called from the same thread
 // (https://crbug.com/130984). Therefore, a SingleThreadedTaskRunner is
-// maintained and accessible through GetDBusTaskRunner(), from which all calls
+// maintained and accessible through GetTaskRunner(), from which all calls
 // to dbus on Linux have to be made.
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -22,6 +22,7 @@
 
 namespace dbus_thread_linux {
 
+// Obtains a task runner to handle DBus IO for usage on desktop Linux.
 COMPONENT_EXPORT(DBUS)
 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner();
 
diff --git a/components/dbus/utils/BUILD.gn b/components/dbus/utils/BUILD.gn
new file mode 100644
index 0000000..9cd8a0f
--- /dev/null
+++ b/components/dbus/utils/BUILD.gn
@@ -0,0 +1,15 @@
+# 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.
+
+component("utils") {
+  sources = [
+    "name_has_owner.cc",
+    "name_has_owner.h",
+  ]
+  defines = [ "IS_DBUS_IMPL" ]
+  deps = [
+    "//base",
+    "//dbus",
+  ]
+}
diff --git a/components/dbus/utils/name_has_owner.cc b/components/dbus/utils/name_has_owner.cc
new file mode 100644
index 0000000..6cf4456
--- /dev/null
+++ b/components/dbus/utils/name_has_owner.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 "components/dbus/utils/name_has_owner.h"
+
+#include <utility>
+
+#include "base/functional/callback.h"
+#include "base/logging.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_proxy.h"
+
+namespace dbus_utils {
+
+namespace {
+
+constexpr char kMethodNameHasOwner[] = "NameHasOwner";
+
+void OnNameHasOwnerResponse(NameHasOwnerCallback callback,
+                            dbus::Response* response) {
+  std::optional<bool> result;
+  if (response) {
+    dbus::MessageReader reader(response);
+    bool name_has_owner = false;
+    if (reader.PopBool(&name_has_owner)) {
+      result = name_has_owner;
+    } else {
+      LOG(ERROR) << "Failed to read " << kMethodNameHasOwner << " response";
+    }
+  }
+  std::move(callback).Run(result);
+}
+
+}  // namespace
+
+void NameHasOwner(dbus::Bus* bus,
+                  const std::string& name,
+                  NameHasOwnerCallback callback) {
+  dbus::ObjectProxy* proxy =
+      bus->GetObjectProxy(DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS));
+  dbus::MethodCall method_call(DBUS_INTERFACE_DBUS, kMethodNameHasOwner);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(name);
+
+  proxy->CallMethod(
+      &method_call, DBUS_TIMEOUT_USE_DEFAULT,
+      base::BindOnce(OnNameHasOwnerResponse, std::move(callback)));
+}
+
+}  // namespace dbus_utils
diff --git a/components/dbus/utils/name_has_owner.h b/components/dbus/utils/name_has_owner.h
new file mode 100644
index 0000000..fc38847
--- /dev/null
+++ b/components/dbus/utils/name_has_owner.h
@@ -0,0 +1,33 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DBUS_UTILS_NAME_HAS_OWNER_H_
+#define COMPONENTS_DBUS_UTILS_NAME_HAS_OWNER_H_
+
+#include <optional>
+#include <string>
+
+#include "base/component_export.h"
+#include "base/functional/callback_forward.h"
+#include "dbus/object_path.h"
+
+namespace dbus {
+class Bus;
+}
+
+namespace dbus_utils {
+
+using NameHasOwnerCallback =
+    base::OnceCallback<void(/*name_has_owner=*/std::optional<bool>)>;
+
+// Checks whether the service `name` has an owner on the bus and runs `callback`
+// with this boolean value. Errors are indicated as a nullopt.
+COMPONENT_EXPORT(DBUS)
+void NameHasOwner(dbus::Bus* bus,
+                  const std::string& name,
+                  NameHasOwnerCallback callback);
+
+}  // namespace dbus_utils
+
+#endif  // COMPONENTS_DBUS_UTILS_NAME_HAS_OWNER_H_
diff --git a/components/desks_storage/DEPS b/components/desks_storage/DEPS
index 9b376351..7852f21 100644
--- a/components/desks_storage/DEPS
+++ b/components/desks_storage/DEPS
@@ -15,6 +15,7 @@
   "+components/tab_groups",
   "+components/version_info",
   "+third_party/re2",
+  "+ui/base/mojom",
   "+ui/base/ui_base_types.h",
   "+ui/base/window_open_disposition.h",
   "+ui/gfx/geometry",
diff --git a/components/desks_storage/core/desk_template_conversion.cc b/components/desks_storage/core/desk_template_conversion.cc
index 0551dd2e..3dc417b0 100644
--- a/components/desks_storage/core/desk_template_conversion.cc
+++ b/components/desks_storage/core/desk_template_conversion.cc
@@ -28,6 +28,7 @@
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_info.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "ui/gfx/geometry/rect.h"
 
 #if !BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -513,25 +514,25 @@
   return base::Contains(kValidWindowStates, window_state);
 }
 
-// Convert JSON string WindowState `state` to ui::WindowShowState used by
+// Convert JSON string WindowState `state` to ui::mojom::WindowShowState used by
 // the app_restore::WindowInfo struct.
-ui::WindowShowState ToUiWindowState(const std::string& window_state) {
+ui::mojom::WindowShowState ToUiWindowState(const std::string& window_state) {
   if (window_state == kWindowStateNormal)
-    return ui::WindowShowState::SHOW_STATE_NORMAL;
+    return ui::mojom::WindowShowState::kNormal;
   else if (window_state == kWindowStateMinimized)
-    return ui::WindowShowState::SHOW_STATE_MINIMIZED;
+    return ui::mojom::WindowShowState::kMinimized;
   else if (window_state == kWindowStateMaximized)
-    return ui::WindowShowState::SHOW_STATE_MAXIMIZED;
+    return ui::mojom::WindowShowState::kMaximized;
   else if (window_state == kWindowStateFullscreen)
-    return ui::WindowShowState::SHOW_STATE_FULLSCREEN;
+    return ui::mojom::WindowShowState::kFullscreen;
   else if (window_state == kWindowStatePrimarySnapped)
-    return ui::WindowShowState::SHOW_STATE_NORMAL;
+    return ui::mojom::WindowShowState::kNormal;
   else if (window_state == kWindowStateSecondarySnapped)
-    return ui::WindowShowState::SHOW_STATE_NORMAL;
+    return ui::mojom::WindowShowState::kNormal;
   // We should never reach here unless we have been passed an invalid window
   // state
   DCHECK(IsValidWindowState(window_state));
-  return ui::WindowShowState::SHOW_STATE_NORMAL;
+  return ui::mojom::WindowShowState::kNormal;
 }
 
 // Convert JSON string WindowState `state` to chromeos::WindowStateType used by
@@ -732,17 +733,18 @@
   }
 }
 
-// Convert ui::WindowShowState `state` to JSON used by the base::Value
+// Convert ui::mojom::WindowShowState `state` to JSON used by the base::Value
 // representation.
-std::string UiWindowStateToString(const ui::WindowShowState& window_state) {
+std::string UiWindowStateToString(
+    const ui::mojom::WindowShowState& window_state) {
   switch (window_state) {
-    case ui::WindowShowState::SHOW_STATE_NORMAL:
+    case ui::mojom::WindowShowState::kNormal:
       return kWindowStateNormal;
-    case ui::WindowShowState::SHOW_STATE_MINIMIZED:
+    case ui::mojom::WindowShowState::kMinimized:
       return kWindowStateMinimized;
-    case ui::WindowShowState::SHOW_STATE_MAXIMIZED:
+    case ui::mojom::WindowShowState::kMaximized:
       return kWindowStateMaximized;
-    case ui::WindowShowState::SHOW_STATE_FULLSCREEN:
+    case ui::mojom::WindowShowState::kFullscreen:
       return kWindowStateFullscreen;
     default:
       // available states in JSON representation is a subset
@@ -1348,26 +1350,26 @@
   }
 }
 
-// Convert Sync proto WindowState `state` to ui::WindowShowState used by
+// Convert Sync proto WindowState `state` to ui::mojom::WindowShowState used by
 // the app_restore::WindowInfo struct.
-ui::WindowShowState ToUiWindowState(WindowState state) {
+ui::mojom::WindowShowState ToUiWindowState(WindowState state) {
   switch (state) {
     case WindowState::WorkspaceDeskSpecifics_WindowState_UNKNOWN_WINDOW_STATE:
-      return ui::WindowShowState::SHOW_STATE_NORMAL;
+      return ui::mojom::WindowShowState::kNormal;
     case WindowState::WorkspaceDeskSpecifics_WindowState_NORMAL:
-      return ui::WindowShowState::SHOW_STATE_NORMAL;
+      return ui::mojom::WindowShowState::kNormal;
     case WindowState::WorkspaceDeskSpecifics_WindowState_MINIMIZED:
-      return ui::WindowShowState::SHOW_STATE_MINIMIZED;
+      return ui::mojom::WindowShowState::kMinimized;
     case WindowState::WorkspaceDeskSpecifics_WindowState_MAXIMIZED:
-      return ui::WindowShowState::SHOW_STATE_MAXIMIZED;
+      return ui::mojom::WindowShowState::kMaximized;
     case WindowState::WorkspaceDeskSpecifics_WindowState_FULLSCREEN:
-      return ui::WindowShowState::SHOW_STATE_FULLSCREEN;
+      return ui::mojom::WindowShowState::kFullscreen;
     case WindowState::WorkspaceDeskSpecifics_WindowState_PRIMARY_SNAPPED:
-      return ui::WindowShowState::SHOW_STATE_NORMAL;
+      return ui::mojom::WindowShowState::kNormal;
     case WindowState::WorkspaceDeskSpecifics_WindowState_SECONDARY_SNAPPED:
-      return ui::WindowShowState::SHOW_STATE_NORMAL;
+      return ui::mojom::WindowShowState::kNormal;
     case WindowState::WorkspaceDeskSpecifics_WindowState_FLOATED:
-      return ui::WindowShowState::SHOW_STATE_NORMAL;
+      return ui::mojom::WindowShowState::kNormal;
   }
 }
 
@@ -1420,19 +1422,19 @@
   }
 }
 
-// Convert ui::WindowShowState to Sync proto WindowState.
-WindowState FromUiWindowState(ui::WindowShowState state) {
+// Convert ui::mojom::WindowShowState to Sync proto WindowState.
+WindowState FromUiWindowState(ui::mojom::WindowShowState state) {
   switch (state) {
-    case ui::WindowShowState::SHOW_STATE_DEFAULT:
-    case ui::WindowShowState::SHOW_STATE_NORMAL:
-    case ui::WindowShowState::SHOW_STATE_INACTIVE:
-    case ui::WindowShowState::SHOW_STATE_END:
+    case ui::mojom::WindowShowState::kDefault:
+    case ui::mojom::WindowShowState::kNormal:
+    case ui::mojom::WindowShowState::kInactive:
+    case ui::mojom::WindowShowState::kEnd:
       return WindowState::WorkspaceDeskSpecifics_WindowState_NORMAL;
-    case ui::WindowShowState::SHOW_STATE_MINIMIZED:
+    case ui::mojom::WindowShowState::kMinimized:
       return WindowState::WorkspaceDeskSpecifics_WindowState_MINIMIZED;
-    case ui::WindowShowState::SHOW_STATE_MAXIMIZED:
+    case ui::mojom::WindowShowState::kMaximized:
       return WindowState::WorkspaceDeskSpecifics_WindowState_MAXIMIZED;
-    case ui::WindowShowState::SHOW_STATE_FULLSCREEN:
+    case ui::mojom::WindowShowState::kFullscreen:
       return WindowState::WorkspaceDeskSpecifics_WindowState_FULLSCREEN;
   }
 }
diff --git a/components/desks_storage/core/desk_template_conversion_unittests.cc b/components/desks_storage/core/desk_template_conversion_unittests.cc
index d7300a9d..e5b4152 100644
--- a/components/desks_storage/core/desk_template_conversion_unittests.cc
+++ b/components/desks_storage/core/desk_template_conversion_unittests.cc
@@ -30,6 +30,7 @@
 #include "components/tab_groups/tab_group_info.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 
 namespace desks_storage {
 
@@ -344,7 +345,7 @@
   EXPECT_THAT(wi->window_state_type,
               testing::Optional(chromeos::WindowStateType::kMinimized));
   EXPECT_THAT(wi->pre_minimized_show_state_type,
-              testing::Optional(ui::WindowShowState::SHOW_STATE_NORMAL));
+              testing::Optional(ui::mojom::WindowShowState::kNormal));
   EXPECT_THAT(wi->current_bounds, testing::Optional(gfx::Rect(0, 1, 120, 121)));
 }
 
diff --git a/components/desks_storage/core/saved_desk_builder.cc b/components/desks_storage/core/saved_desk_builder.cc
index 734f42a..1a4edb3 100644
--- a/components/desks_storage/core/saved_desk_builder.cc
+++ b/components/desks_storage/core/saved_desk_builder.cc
@@ -12,6 +12,7 @@
 #include "components/app_restore/app_launch_info.h"
 #include "components/desks_storage/core/desk_template_conversion.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 
 namespace desks_storage {
 
@@ -61,7 +62,7 @@
 
 SavedDeskGenericAppBuilder&
 SavedDeskGenericAppBuilder::SetPreMinimizedWindowState(
-    ui::WindowShowState state) {
+    ui::mojom::WindowShowState state) {
   pre_minimized_window_show_state_ = state;
   return *this;
 }
diff --git a/components/desks_storage/core/saved_desk_builder.h b/components/desks_storage/core/saved_desk_builder.h
index abc7e47c..3cd2d495 100644
--- a/components/desks_storage/core/saved_desk_builder.h
+++ b/components/desks_storage/core/saved_desk_builder.h
@@ -19,6 +19,7 @@
 #include "components/sync/protocol/workspace_desk_specifics.pb.h"
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_info.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/rect.h"
@@ -78,7 +79,7 @@
   SavedDeskGenericAppBuilder& SetWindowBound(gfx::Rect bounds);
   SavedDeskGenericAppBuilder& SetWindowState(chromeos::WindowStateType state);
   SavedDeskGenericAppBuilder& SetPreMinimizedWindowState(
-      ui::WindowShowState state);
+      ui::mojom::WindowShowState state);
   SavedDeskGenericAppBuilder& SetZIndex(int index);
   SavedDeskGenericAppBuilder& SetWindowId(int window_id);
   SavedDeskGenericAppBuilder& SetDisplayId(int64_t display_id);
@@ -101,7 +102,7 @@
   std::optional<std::string> app_id_;
   std::optional<gfx::Rect> window_bounds_;
   std::optional<chromeos::WindowStateType> window_show_state_;
-  std::optional<ui::WindowShowState> pre_minimized_window_show_state_;
+  std::optional<ui::mojom::WindowShowState> pre_minimized_window_show_state_;
   std::optional<int> z_index_;
   std::optional<int> window_id_;
   std::optional<int64_t> display_id_;
diff --git a/components/device_signals/core/browser/signals_types.cc b/components/device_signals/core/browser/signals_types.cc
index b494a71..c609cdf 100644
--- a/components/device_signals/core/browser/signals_types.cc
+++ b/components/device_signals/core/browser/signals_types.cc
@@ -123,11 +123,15 @@
 AgentSignalsResponse::~AgentSignalsResponse() = default;
 
 SignalsAggregationRequest::SignalsAggregationRequest() = default;
+
 SignalsAggregationRequest::SignalsAggregationRequest(
     const SignalsAggregationRequest&) = default;
 
+SignalsAggregationRequest::SignalsAggregationRequest(
+    SignalsAggregationRequest&&) = default;
+
 SignalsAggregationRequest& SignalsAggregationRequest::operator=(
-    const SignalsAggregationRequest&) = default;
+    SignalsAggregationRequest&&) = default;
 
 SignalsAggregationRequest::~SignalsAggregationRequest() = default;
 
@@ -139,12 +143,19 @@
 }
 
 SignalsAggregationResponse::SignalsAggregationResponse() = default;
+
 SignalsAggregationResponse::SignalsAggregationResponse(
     const SignalsAggregationResponse&) = default;
 
+SignalsAggregationResponse::SignalsAggregationResponse(
+    SignalsAggregationResponse&&) = default;
+
 SignalsAggregationResponse& SignalsAggregationResponse::operator=(
     const SignalsAggregationResponse&) = default;
 
+SignalsAggregationResponse& SignalsAggregationResponse::operator=(
+    SignalsAggregationResponse&&) = default;
+
 SignalsAggregationResponse::~SignalsAggregationResponse() = default;
 
 }  // namespace device_signals
diff --git a/components/device_signals/core/browser/signals_types.h b/components/device_signals/core/browser/signals_types.h
index 13687e7..c8eafc5 100644
--- a/components/device_signals/core/browser/signals_types.h
+++ b/components/device_signals/core/browser/signals_types.h
@@ -206,7 +206,9 @@
   SignalsAggregationRequest();
 
   SignalsAggregationRequest(const SignalsAggregationRequest&);
+  SignalsAggregationRequest(SignalsAggregationRequest&&);
   SignalsAggregationRequest& operator=(const SignalsAggregationRequest&);
+  SignalsAggregationRequest& operator=(SignalsAggregationRequest&&);
 
   ~SignalsAggregationRequest();
 
@@ -229,7 +231,9 @@
   SignalsAggregationResponse();
 
   SignalsAggregationResponse(const SignalsAggregationResponse&);
+  SignalsAggregationResponse(SignalsAggregationResponse&&);
   SignalsAggregationResponse& operator=(const SignalsAggregationResponse&);
+  SignalsAggregationResponse& operator=(SignalsAggregationResponse&&);
 
   ~SignalsAggregationResponse();
 
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index 502ee65..c0f039e 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -55,6 +55,7 @@
 #include "ui/aura/window_observer.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/class_property.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/compositor_lock.h"
 #include "ui/compositor/layer.h"
@@ -175,19 +176,19 @@
         break;
       case chromeos::WindowStateType::kFullscreen:
         switch (window->GetProperty(aura::client::kRestoreShowStateKey)) {
-          case ui::SHOW_STATE_DEFAULT:
-          case ui::SHOW_STATE_NORMAL:
+          case ui::mojom::WindowShowState::kDefault:
+          case ui::mojom::WindowShowState::kNormal:
             next_state = chromeos::WindowStateType::kNormal;
             break;
-          case ui::SHOW_STATE_MAXIMIZED:
+          case ui::mojom::WindowShowState::kMaximized:
             next_state = chromeos::WindowStateType::kMaximized;
             break;
-          case ui::SHOW_STATE_MINIMIZED:
+          case ui::mojom::WindowShowState::kMinimized:
             next_state = chromeos::WindowStateType::kMinimized;
             break;
-          case ui::SHOW_STATE_FULLSCREEN:
-          case ui::SHOW_STATE_INACTIVE:
-          case ui::SHOW_STATE_END:
+          case ui::mojom::WindowShowState::kFullscreen:
+          case ui::mojom::WindowShowState::kInactive:
+          case ui::mojom::WindowShowState::kEnd:
             DUMP_WILL_BE_NOTREACHED()
                 << " unknown state :"
                 << window->GetProperty(aura::client::kRestoreShowStateKey);
@@ -477,7 +478,7 @@
                static_cast<int>(type));
 
   if (!widget_)
-    CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
+    CreateShellSurfaceWidget(ui::mojom::WindowShowState::kNormal);
 
   if (type == chromeos::WindowPinType::kNone) {
     // Set other window state mode will automatically cancelled pin mode.
@@ -494,7 +495,7 @@
                "autohide", autohide);
 
   if (!widget_)
-    CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
+    CreateShellSurfaceWidget(ui::mojom::WindowShowState::kNormal);
 
   ash::window_util::SetAutoHideShelf(widget_->GetNativeWindow(), autohide);
 }
@@ -941,12 +942,12 @@
 
 void ClientControlledShellSurface::SaveWindowPlacement(
     const gfx::Rect& bounds,
-    ui::WindowShowState show_state) {}
+    ui::mojom::WindowShowState show_state) {}
 
 bool ClientControlledShellSurface::GetSavedWindowPlacement(
     const views::Widget* widget,
     gfx::Rect* bounds,
-    ui::WindowShowState* show_state) const {
+    ui::mojom::WindowShowState* show_state) const {
   return false;
 }
 
diff --git a/components/exo/client_controlled_shell_surface.h b/components/exo/client_controlled_shell_surface.h
index c31516c..1f29d64 100644
--- a/components/exo/client_controlled_shell_surface.h
+++ b/components/exo/client_controlled_shell_surface.h
@@ -17,6 +17,7 @@
 #include "components/exo/client_controlled_accelerators.h"
 #include "components/exo/shell_surface_base.h"
 #include "ui/base/hit_test.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/compositor/compositor_lock.h"
 
 namespace ash {
@@ -199,10 +200,11 @@
       views::Widget* widget) override;
   bool ShouldSaveWindowPlacement() const override;
   void SaveWindowPlacement(const gfx::Rect& bounds,
-                           ui::WindowShowState show_state) override;
-  bool GetSavedWindowPlacement(const views::Widget* widget,
-                               gfx::Rect* bounds,
-                               ui::WindowShowState* show_state) const override;
+                           ui::mojom::WindowShowState show_state) override;
+  bool GetSavedWindowPlacement(
+      const views::Widget* widget,
+      gfx::Rect* bounds,
+      ui::mojom::WindowShowState* show_state) const override;
 
   // views::View:
   gfx::Size GetMaximumSize() const override;
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index eccca9ff..27971234 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -35,6 +35,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/compositor/layer.h"
 #include "ui/views/widget/widget.h"
@@ -251,9 +252,10 @@
   TRACE_EVENT0("exo", "ShellSurface::Maximize");
 
   if (!widget_) {
-    if (initial_show_state_ != ui::SHOW_STATE_FULLSCREEN ||
-        ShouldExitFullscreenFromRestoreOrMaximized())
-      initial_show_state_ = ui::SHOW_STATE_MAXIMIZED;
+    if (initial_show_state_ != ui::mojom::WindowShowState::kFullscreen ||
+        ShouldExitFullscreenFromRestoreOrMaximized()) {
+      initial_show_state_ = ui::mojom::WindowShowState::kMaximized;
+    }
     return;
   }
 
@@ -270,7 +272,7 @@
   TRACE_EVENT0("exo", "ShellSurface::Minimize");
 
   if (!widget_) {
-    initial_show_state_ = ui::SHOW_STATE_MINIMIZED;
+    initial_show_state_ = ui::mojom::WindowShowState::kMinimized;
     return;
   }
 
@@ -284,9 +286,10 @@
   TRACE_EVENT0("exo", "ShellSurface::Restore");
 
   if (!widget_) {
-    if (initial_show_state_ != ui::SHOW_STATE_FULLSCREEN ||
-        ShouldExitFullscreenFromRestoreOrMaximized())
-      initial_show_state_ = ui::SHOW_STATE_NORMAL;
+    if (initial_show_state_ != ui::mojom::WindowShowState::kFullscreen ||
+        ShouldExitFullscreenFromRestoreOrMaximized()) {
+      initial_show_state_ = ui::mojom::WindowShowState::kNormal;
+    }
     return;
   }
 
@@ -304,9 +307,9 @@
                "display_id", display_id);
   if (!widget_) {
     if (fullscreen) {
-      initial_show_state_ = ui::SHOW_STATE_FULLSCREEN;
-    } else if (initial_show_state_ == ui::SHOW_STATE_FULLSCREEN) {
-      initial_show_state_ = ui::SHOW_STATE_DEFAULT;
+      initial_show_state_ = ui::mojom::WindowShowState::kFullscreen;
+    } else if (initial_show_state_ == ui::mojom::WindowShowState::kFullscreen) {
+      initial_show_state_ = ui::mojom::WindowShowState::kDefault;
     }
     return;
   }
@@ -901,8 +904,9 @@
         root_surface()->surface_hierarchy_content_bounds().IsEmpty()) {
       Configure();
 
-      if (initial_show_state_ != ui::SHOW_STATE_MINIMIZED)
+      if (initial_show_state_ != ui::mojom::WindowShowState::kMinimized) {
         needs_layout_on_show_ = true;
+      }
     }
 
     CreateShellSurfaceWidget(initial_show_state_);
diff --git a/components/exo/shell_surface.h b/components/exo/shell_surface.h
index 5b358e9..869751d6 100644
--- a/components/exo/shell_surface.h
+++ b/components/exo/shell_surface.h
@@ -16,6 +16,7 @@
 #include "base/observer_list.h"
 #include "components/exo/shell_surface_base.h"
 #include "components/exo/shell_surface_observer.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "ui/base/ui_base_types.h"
 
 namespace wm {
@@ -140,7 +141,9 @@
                                bool enable_wrapping) override;
 
   // Return the initial show state for this surface.
-  ui::WindowShowState initial_show_state() { return initial_show_state_; }
+  ui::mojom::WindowShowState initial_show_state() {
+    return initial_show_state_;
+  }
 
   void AddObserver(ShellSurfaceObserver* observer);
   void RemoveObserver(ShellSurfaceObserver* observer);
@@ -320,7 +323,8 @@
   int resize_component_ = HTCAPTION;  // HT constant (see ui/base/hit_test.h)
   int pending_resize_component_ = HTCAPTION;
   // TODO(oshima): Use WindowStateType instead.
-  ui::WindowShowState initial_show_state_ = ui::SHOW_STATE_DEFAULT;
+  ui::mojom::WindowShowState initial_show_state_ =
+      ui::mojom::WindowShowState::kDefault;
   bool notify_bounds_changes_ = true;
   bool window_state_is_changing_ = false;
   float pending_raster_scale_ = 1.0;
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 5d57355..f4cd8d4 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -55,6 +55,7 @@
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/class_property.h"
 #include "ui/base/mojom/ui_base_types.mojom-shared.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor_extra/shadow.h"
 #include "ui/display/display.h"
@@ -1720,7 +1721,7 @@
 // ShellSurfaceBase, protected:
 
 void ShellSurfaceBase::CreateShellSurfaceWidget(
-    ui::WindowShowState show_state) {
+    ui::mojom::WindowShowState show_state) {
   DCHECK(GetEnabled());
   DCHECK(!widget_);
 
diff --git a/components/exo/shell_surface_base.h b/components/exo/shell_surface_base.h
index 4424e3d3..530a904 100644
--- a/components/exo/shell_surface_base.h
+++ b/components/exo/shell_surface_base.h
@@ -25,6 +25,7 @@
 #include "ui/aura/client/capture_client_observer.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/hit_test.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
@@ -391,7 +392,7 @@
 
   // Creates the |widget_| for |surface_|. |show_state| is the initial state
   // of the widget (e.g. maximized).
-  void CreateShellSurfaceWidget(ui::WindowShowState show_state);
+  void CreateShellSurfaceWidget(ui::mojom::WindowShowState show_state);
 
   // Returns true if the window is the ShellSurface's widget's window.
   bool IsShellSurfaceWindow(const aura::Window* window) const;
diff --git a/components/exo/xdg_shell_surface.cc b/components/exo/xdg_shell_surface.cc
index dba2bb2..2377c40e 100644
--- a/components/exo/xdg_shell_surface.cc
+++ b/components/exo/xdg_shell_surface.cc
@@ -6,6 +6,7 @@
 
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "chromeos/ui/base/window_properties.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/rect.h"
@@ -33,7 +34,7 @@
   bool auto_maximize_enabled = params->init_properties_container.GetProperty(
       chromeos::kAutoMaximizeXdgShellEnabled);
   if (auto_maximize_enabled && ShouldAutoMaximize()) {
-    params->show_state = ui::SHOW_STATE_MAXIMIZED;
+    params->show_state = ui::mojom::WindowShowState::kMaximized;
   }
   if (!frame_enabled() && !has_frame_colors()) {
     params->layer_type = ui::LAYER_NOT_DRAWN;
@@ -41,9 +42,10 @@
 }
 
 bool XdgShellSurface::ShouldAutoMaximize() {
-  if (initial_show_state() != ui::SHOW_STATE_DEFAULT || is_popup_ ||
-      !CanMaximize())
+  if (initial_show_state() != ui::mojom::WindowShowState::kDefault ||
+      is_popup_ || !CanMaximize()) {
     return false;
+  }
 
   DCHECK(!widget_);
   gfx::Size work_area_size = display::Screen::GetScreen()
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
index 2f12a9e..ad80464 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
@@ -221,6 +221,8 @@
   // doesn't support it yet.
   if (client_->IsInLandscapeMode() &&
       !base::FeatureList::IsEnabled(kEnablePixPaymentsInLandscapeMode)) {
+    LogPaymentNotOfferedReason(
+        PaymentNotOfferedReason::kLandscapeScreenOrientation);
     Reset();
     return;
   }
@@ -311,8 +313,7 @@
   LogLoadRiskDataResultAndLatency(/*was_successful=*/!risk_data.empty(),
                                   base::TimeTicks::Now() - start_time);
   if (risk_data.empty()) {
-    // TODO: b/348143700 - Show error screen if the loading screen is being
-    // shown.
+    client_->ShowErrorScreen();
     LogPaymentNotOfferedReason(PaymentNotOfferedReason::kRiskDataEmpty);
     Reset();
     return;
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.h b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
index 3fdcf07..cb4f59e6 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.h
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
@@ -131,8 +131,9 @@
                            PixPaymentPromptAccepted_TriggersLoadRiskData);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            PaymentNotOfferedReason_RiskDataEmpty);
-  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
-                           RiskDataEmpty_GetClientTokenNotCalled);
+  FRIEND_TEST_ALL_PREFIXES(
+      FacilitatedPaymentsManagerTest,
+      RiskDataEmpty_GetClientTokenNotCalled_ErrorScreenShown);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            RiskDataNotEmpty_GetClientTokenCalled);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
@@ -237,6 +238,8 @@
                            HandlesFailureToLazilyInitializeApiClient);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTestInLandscapeMode,
                            PixPayflowBlockedWhenFlagDisabled);
+  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTestInLandscapeMode,
+                           HistogramForPaymentNotOfferedReason);
 
   // Register optimization guide deciders for PIX. It is an allowlist of URLs
   // where we attempt PIX code detection.
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
index 5a67617..2885a65d 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
@@ -959,8 +959,10 @@
 
 // If the risk data is empty, then the manager does not retrieve a client token
 // from the facilitated payments API client.
-TEST_F(FacilitatedPaymentsManagerTest, RiskDataEmpty_GetClientTokenNotCalled) {
+TEST_F(FacilitatedPaymentsManagerTest,
+       RiskDataEmpty_GetClientTokenNotCalled_ErrorScreenShown) {
   EXPECT_CALL(GetApiClient(), GetClientToken(testing::_)).Times(0);
+  EXPECT_CALL(*client_, ShowErrorScreen());
 
   manager_->OnRiskDataLoaded(/*start_time=*/base::TimeTicks::Now(),
                              /*risk_data=*/"");
@@ -1684,13 +1686,18 @@
     : public FacilitatedPaymentsManagerTest,
       public testing::WithParamInterface<bool> {
  public:
-  void SetUp() override {
-    FacilitatedPaymentsManagerTest::SetUp();
-    ON_CALL(*client_, IsInLandscapeMode).WillByDefault(testing::Return(true));
+  FacilitatedPaymentsManagerTestInLandscapeMode() {
     scoped_feature_list_.InitWithFeatureState(kEnablePixPaymentsInLandscapeMode,
                                               GetParam());
   }
 
+  void SetUp() override {
+    FacilitatedPaymentsManagerTest::SetUp();
+    ON_CALL(*client_, IsInLandscapeMode).WillByDefault(testing::Return(true));
+  }
+
+  bool IsPaymentEnabledInLandscapeMode() { return GetParam(); }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -1707,11 +1714,28 @@
   // Pix payflow) is only done if the `EnablePixPaymentsInLandscapeMode` flag is
   // enabled.
   EXPECT_CALL(GetApiClient(), IsAvailable(testing::_))
-      .Times(GetParam() ? 1 : 0);
+      .Times(IsPaymentEnabledInLandscapeMode() ? 1 : 0);
 
   manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
                                base::TimeTicks::Now(),
                                /*is_pix_code_valid=*/true);
 }
 
+TEST_P(FacilitatedPaymentsManagerTestInLandscapeMode,
+       HistogramForPaymentNotOfferedReason) {
+  base::HistogramTester histogram_tester;
+  payments_data_manager_->AddMaskedBankAccountForTest(CreatePixBankAccount(1));
+
+  manager_->OnPixCodeValidated(/*pix_code=*/std::string(),
+                               base::TimeTicks::Now(),
+                               /*is_pix_code_valid=*/true);
+
+  // In landscape mode, if the `EnablePixPaymentsInLandscapeMode` flag is
+  // disabled, Pix payment is not offered, and a histogram should be logged.
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Pix.PaymentNotOfferedReason",
+      /*sample=*/PaymentNotOfferedReason::kLandscapeScreenOrientation,
+      /*expected_bucket_count=*/IsPaymentEnabledInLandscapeMode() ? 0 : 1);
+}
+
 }  // namespace payments::facilitated
diff --git a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
index 001ee33..e84fa7ec 100644
--- a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
+++ b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
@@ -22,7 +22,8 @@
   kRiskDataEmpty = 1,
   kCodeValidatorFailed = 2,
   kInvalidCode = 3,
-  kMaxValue = kInvalidCode
+  kLandscapeScreenOrientation = 4,
+  kMaxValue = kLandscapeScreenOrientation
 };
 
 // Result of the transaction from the time payment was offered to the user.
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h
index c782f514..851d9ac 100644
--- a/components/feed/core/v2/public/types.h
+++ b/components/feed/core/v2/public/types.h
@@ -74,9 +74,11 @@
 
 struct NetworkResponseInfo {
   NetworkResponseInfo();
-  ~NetworkResponseInfo();
   NetworkResponseInfo(const NetworkResponseInfo&);
+  NetworkResponseInfo(NetworkResponseInfo&&);
   NetworkResponseInfo& operator=(const NetworkResponseInfo&);
+  NetworkResponseInfo& operator=(NetworkResponseInfo&&);
+  ~NetworkResponseInfo();
 
   // A union of net::Error (if the request failed) and the http
   // status code(if the request succeeded in reaching the server).
diff --git a/components/feed/core/v2/types.cc b/components/feed/core/v2/types.cc
index 5aa21df..910814a 100644
--- a/components/feed/core/v2/types.cc
+++ b/components/feed/core/v2/types.cc
@@ -123,10 +123,13 @@
 }
 
 NetworkResponseInfo::NetworkResponseInfo() = default;
-NetworkResponseInfo::~NetworkResponseInfo() = default;
 NetworkResponseInfo::NetworkResponseInfo(const NetworkResponseInfo&) = default;
+NetworkResponseInfo::NetworkResponseInfo(NetworkResponseInfo&&) = default;
 NetworkResponseInfo& NetworkResponseInfo::operator=(
     const NetworkResponseInfo&) = default;
+NetworkResponseInfo& NetworkResponseInfo::operator=(NetworkResponseInfo&&) =
+    default;
+NetworkResponseInfo::~NetworkResponseInfo() = default;
 
 NetworkResponse::NetworkResponse() = default;
 NetworkResponse::NetworkResponse(const std::string& response_bytes,
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index 988af9e6..4a6ce45 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -163,10 +163,6 @@
 // deleting some.
 const int kMaxRedirectCount = 32;
 
-// The number of days old a history entry can be before it is considered "old"
-// and is deleted.
-constexpr int kExpireDaysThreshold = 90;
-
 // The maximum number of days for which domain visit metrics are computed
 // each time HistoryBackend::GetDomainDiversity() is called.
 constexpr int kDomainDiversityMaxBacktrackedDays = 7;
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h
index 7106e3e..89f7c567 100644
--- a/components/history/core/browser/history_backend.h
+++ b/components/history/core/browser/history_backend.h
@@ -206,6 +206,10 @@
   // Check if the transition should increment the typed_count of a visit.
   static bool IsTypedIncrement(ui::PageTransition transition);
 
+  // The number of days old a history entry can be before it is considered "old"
+  // and is deleted.
+  static constexpr int kExpireDaysThreshold = 90;
+
   // Init must be called to complete object creation. This object can be
   // constructed on any thread, but all other functions including Init() must
   // be called on the history thread.
diff --git a/components/history_clusters/history_clusters_internals/webui/history_clusters_internals_ui.cc b/components/history_clusters/history_clusters_internals/webui/history_clusters_internals_ui.cc
index 0bca519..1c7c4e0 100644
--- a/components/history_clusters/history_clusters_internals/webui/history_clusters_internals_ui.cc
+++ b/components/history_clusters/history_clusters_internals/webui/history_clusters_internals_ui.cc
@@ -23,8 +23,7 @@
       history_service_(history_service) {
   // TODO(crbug.com/354691088): Explicit use of size when making span is unsafe.
   std::move(set_up_data_source_callback)
-      .Run(base::make_span(kHistoryClustersInternalsResources,
-                           kHistoryClustersInternalsResourcesSize),
+      .Run(base::make_span(kHistoryClustersInternalsResources),
            IDR_HISTORY_CLUSTERS_INTERNALS_HISTORY_CLUSTERS_INTERNALS_HTML);
 }
 
diff --git a/components/js_injection/browser/navigation_web_message_sender.cc b/components/js_injection/browser/navigation_web_message_sender.cc
index 9aefe9da..8240f6d 100644
--- a/components/js_injection/browser/navigation_web_message_sender.cc
+++ b/components/js_injection/browser/navigation_web_message_sender.cc
@@ -4,7 +4,9 @@
 
 #include "components/js_injection/browser/navigation_web_message_sender.h"
 
+#include "base/debug/dump_without_crashing.h"
 #include "base/json/json_writer.h"
+#include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/back_forward_cache/back_forward_cache_disable.h"
 #include "components/back_forward_cache/disabled_reason_id.h"
@@ -15,10 +17,23 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/page.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/url_constants.h"
 #include "net/http/http_response_headers.h"
 
 namespace {
 
+// All navigations that are happening on the primary main frame.
+std::set<int64_t>& GetOngoingPrimaryMainFrameNavigationIds() {
+  static base::NoDestructor<std::set<int64_t>> tracked_ongoing_navigation_ids;
+  return *tracked_ongoing_navigation_ids;
+}
+
+// All navigations, including those happening not on the primary main frame.
+std::set<int64_t>& GetAllOngoingNavigationIds() {
+  static base::NoDestructor<std::set<int64_t>> all_ongoing_navigation_ids;
+  return *all_ongoing_navigation_ids;
+}
+
 base::Value::Dict CreateBaseMessageFromNavigationHandle(
     content::NavigationHandle* navigation_handle) {
   return base::Value::Dict()
@@ -31,6 +46,83 @@
       .Set("isHistory", navigation_handle->IsHistory());
 }
 
+std::string GetURLTypeForCrashKey(const GURL& url) {
+  if (url == content::kUnreachableWebDataURL) {
+    return "error";
+  }
+  if (url == content::kBlockedURL) {
+    return "blocked";
+  }
+  if (url.IsAboutBlank()) {
+    return "about:blank";
+  }
+  if (url.IsAboutSrcdoc()) {
+    return "about:srcdoc";
+  }
+  if (url.is_empty()) {
+    return "empty";
+  }
+  if (!url.is_valid()) {
+    return "invalid";
+  }
+  return url.scheme();
+}
+
+void CheckNavigationIsInPrimaryOngoingList(
+    content::NavigationHandle* navigation_handle,
+    std::string message_type) {
+  if (GetOngoingPrimaryMainFrameNavigationIds().contains(
+          navigation_handle->GetNavigationId())) {
+    return;
+  }
+
+  SCOPED_CRASH_KEY_STRING256("NoTrackedNav", "message", message_type);
+  SCOPED_CRASH_KEY_NUMBER("NoTrackedNav", "nav_id",
+                          navigation_handle->GetNavigationId());
+  SCOPED_CRASH_KEY_STRING32("NoTrackedNav", "url_type",
+                            GetURLTypeForCrashKey(navigation_handle->GetURL()));
+  GURL prev_url = (navigation_handle->HasCommitted()
+                       ? navigation_handle->GetPreviousPrimaryMainFrameURL()
+                       : navigation_handle->GetWebContents()
+                             ->GetPrimaryMainFrame()
+                             ->GetLastCommittedURL());
+  SCOPED_CRASH_KEY_STRING32("NoTrackedNav", "prev_url_type",
+                            GetURLTypeForCrashKey(prev_url));
+
+  std::optional<content::NavigationDiscardReason> discard_reason =
+      navigation_handle->GetNavigationDiscardReason();
+  SCOPED_CRASH_KEY_NUMBER("NoTrackedNav", "discard_reason",
+                          discard_reason.has_value()
+                              ? static_cast<int>(discard_reason.value())
+                              : -1);
+  SCOPED_CRASH_KEY_NUMBER("NoTrackedNav", "primary_navs_size",
+                          GetOngoingPrimaryMainFrameNavigationIds().size());
+  SCOPED_CRASH_KEY_NUMBER("NoTrackedNav", "all_navs_size",
+                          GetAllOngoingNavigationIds().size());
+  SCOPED_CRASH_KEY_NUMBER("NoTrackedNav", "net_error_code",
+                          navigation_handle->GetNetErrorCode());
+
+  SCOPED_CRASH_KEY_BOOL("NoTrackedNav", "has_committed",
+                        navigation_handle->HasCommitted());
+  SCOPED_CRASH_KEY_BOOL("NoTrackedNav", "was_redirect",
+                        navigation_handle->WasServerRedirect());
+  SCOPED_CRASH_KEY_BOOL("NoTrackedNav", "is_activation",
+                        navigation_handle->IsPageActivation());
+  SCOPED_CRASH_KEY_BOOL("NoTrackedNav", "is_same_doc",
+                        navigation_handle->IsSameDocument());
+  SCOPED_CRASH_KEY_BOOL("NoTrackedNav", "is_renderer",
+                        navigation_handle->IsRendererInitiated());
+  SCOPED_CRASH_KEY_BOOL("NoTrackedNav", "is_history",
+                        navigation_handle->IsHistory());
+  SCOPED_CRASH_KEY_BOOL(
+      "NoTrackedNav", "is_reload",
+      navigation_handle->GetReloadType() != content::ReloadType::NONE);
+  SCOPED_CRASH_KEY_BOOL(
+      "NoTrackedNav", "is_restore",
+      navigation_handle->GetRestoreType() == content::RestoreType::kRestored);
+  base::debug::DumpWithoutCrashing();
+}
+
 }  // namespace
 
 namespace features {
@@ -165,9 +257,23 @@
 
 void NavigationWebMessageSender::DidStartNavigation(
     content::NavigationHandle* navigation_handle) {
+  if (page().IsPrimary()) {
+    // Add `navigation_handle` to the list of all ongoing navigations. Note that
+    // we only call this if the NavigationWebMessageSender is for the primary
+    // page, to ensure we only add it to the list once. The navigation itself
+    // might not be on the primary page, but it doesn't matter, since this list
+    // wants to capture all navigations.
+    GetAllOngoingNavigationIds().insert(navigation_handle->GetNavigationId());
+  }
+
   if (!ShouldSendMessageForNavigation(navigation_handle)) {
     return;
   }
+
+  // Add `navigation_handle` to the list of ongoing primary main frame
+  // navigations.
+  GetOngoingPrimaryMainFrameNavigationIds().insert(
+      navigation_handle->GetNavigationId());
   base::Value::Dict message_dict =
       CreateBaseMessageFromNavigationHandle(navigation_handle)
           .Set("type", kNavigationStartedMessage);
@@ -179,6 +285,9 @@
   if (!ShouldSendMessageForNavigation(navigation_handle)) {
     return;
   }
+  CheckNavigationIsInPrimaryOngoingList(navigation_handle,
+                                        kNavigationRedirectedMessage);
+
   base::Value::Dict message_dict =
       CreateBaseMessageFromNavigationHandle(navigation_handle)
           .Set("type", kNavigationRedirectedMessage);
@@ -187,9 +296,20 @@
 
 void NavigationWebMessageSender::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
+  if (page().IsPrimary()) {
+    // Remove `navigation_handle` to the list of all ongoing navigations. Note
+    // that we only call this if the NavigationWebMessageSender is for the
+    // primary page, to ensure we only remove it from the list once.
+    GetAllOngoingNavigationIds().erase(navigation_handle->GetNavigationId());
+  }
   if (!ShouldSendMessageForNavigation(navigation_handle)) {
     return;
   }
+  CheckNavigationIsInPrimaryOngoingList(navigation_handle,
+                                        kNavigationCompletedMessage);
+  GetOngoingPrimaryMainFrameNavigationIds().erase(
+      navigation_handle->GetNavigationId());
+
   base::Value::Dict message_dict =
       CreateBaseMessageFromNavigationHandle(navigation_handle)
           .Set("type", kNavigationCompletedMessage)
diff --git a/components/lens/lens_features.cc b/components/lens/lens_features.cc
index 0079bba2..e4939d9 100644
--- a/components/lens/lens_features.cc
+++ b/components/lens/lens_features.cc
@@ -55,6 +55,10 @@
              "LensOverlayTranslateButton",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kLensOverlayImageContextMenuActions,
+             "LensOverlayImageContextMenuActions",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 const base::FeatureParam<int> kLensOverlayMinRamMb{&kLensOverlay, "min_ram_mb",
                                                    /*default=value=*/-1};
 const base::FeatureParam<std::string> kActivityUrl{
@@ -233,6 +237,18 @@
 constexpr base::FeatureParam<int> kLensOverlayFindBarStringsVariant{
     &kLensOverlay, "find-bar-strings-variant", 0};
 
+constexpr base::FeatureParam<bool>
+    kLensOverlayImageContextMenuActionsEnableCopyAsImage{
+        &kLensOverlayImageContextMenuActions, "enable-copy-as-image", true};
+
+constexpr base::FeatureParam<bool>
+    kLensOverlayImageContextMenuActionsEnableSaveAsImage{
+        &kLensOverlayImageContextMenuActions, "enable-save-as-image", true};
+
+constexpr base::FeatureParam<int>
+    kLensOverlayImageContextMenuActionsTextReceivedTimeout{
+        &kLensOverlayImageContextMenuActions, "text-received-timeout", 2000};
+
 constexpr base::FeatureParam<std::string> kHomepageURLForLens{
     &kLensStandalone, "lens-homepage-url", "https://lens.google.com/v3/"};
 
@@ -611,4 +627,18 @@
   return base::FeatureList::IsEnabled(kLensOverlayTranslateButton);
 }
 
+bool IsLensOverlayCopyAsImageEnabled() {
+  return base::FeatureList::IsEnabled(kLensOverlayImageContextMenuActions) &&
+         kLensOverlayImageContextMenuActionsEnableCopyAsImage.Get();
+}
+
+bool IsLensOverlaySaveAsImageEnabled() {
+  return base::FeatureList::IsEnabled(kLensOverlayImageContextMenuActions) &&
+         kLensOverlayImageContextMenuActionsEnableSaveAsImage.Get();
+}
+
+int GetLensOverlayImageContextMenuActionsTextReceivedTimeout() {
+  return kLensOverlayImageContextMenuActionsTextReceivedTimeout.Get();
+}
+
 }  // namespace lens::features
diff --git a/components/lens/lens_features.h b/components/lens/lens_features.h
index c7caa46e..6c8dc7a3 100644
--- a/components/lens/lens_features.h
+++ b/components/lens/lens_features.h
@@ -516,6 +516,19 @@
 COMPONENT_EXPORT(LENS_FEATURES)
 extern bool IsLensOverlayTranslateButtonEnabled();
 
+// Whether to show the copy as image context menu option.
+COMPONENT_EXPORT(LENS_FEATURES)
+extern bool IsLensOverlayCopyAsImageEnabled();
+
+// Whether to show the save as image context menu option.
+COMPONENT_EXPORT(LENS_FEATURES)
+extern bool IsLensOverlaySaveAsImageEnabled();
+
+// Time to wait for Lens text response before displaying the selected region
+// context menu, in milliseconds.
+COMPONENT_EXPORT(LENS_FEATURES)
+int GetLensOverlayImageContextMenuActionsTextReceivedTimeout();
+
 }  // namespace lens::features
 
 #endif  // COMPONENTS_LENS_LENS_FEATURES_H_
diff --git a/components/live_caption/live_caption_controller.cc b/components/live_caption/live_caption_controller.cc
index e05013e6..69581ad 100644
--- a/components/live_caption/live_caption_controller.cc
+++ b/components/live_caption/live_caption_controller.cc
@@ -74,6 +74,9 @@
 
   enabled_ = IsLiveCaptionEnabled();
   base::UmaHistogramBoolean("Accessibility.LiveCaption2", enabled_);
+
+  MaybeSetLiveCaptionLanguage();
+
   if (enabled_) {
     StartLiveCaption();
   }
@@ -102,7 +105,9 @@
       prefs::kLiveCaptionMaskOffensiveWords, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 
-  // Initially default the language to en-US.
+  // Initially default the language to en-US. The language
+  // preference value will be set to a default language when Live Caption is
+  // enabled for the first time.
   registry->RegisterStringPref(prefs::kLiveCaptionLanguageCode,
                                speech::kUsEnglishLocale,
                                user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
@@ -307,4 +312,27 @@
   caption_bubble_controller_->UpdateCaptionStyle(caption_style_);
 }
 
+void LiveCaptionController::MaybeSetLiveCaptionLanguage() {
+  // If the current Live Caption language is not installed,
+  // reset the Live Caption language code to the application locale or preferred
+  // language if available.
+  if (speech::SodaInstaller::GetInstance() &&
+      profile_prefs_->GetString(prefs::kLiveCaptionLanguageCode) ==
+          speech::kUsEnglishLocale &&
+      speech::SodaInstaller::GetInstance()
+          ->GetLanguagePath(
+              profile_prefs_->GetString(prefs::kLiveCaptionLanguageCode))
+          .empty()) {
+    speech::SodaInstaller::GetInstance()->UnregisterLanguage(
+        speech::kUsEnglishLocale, global_prefs_);
+    speech::SodaInstaller::GetInstance()->RegisterLanguage(
+        speech::GetDefaultLiveCaptionLanguage(application_locale_,
+                                              profile_prefs_),
+        global_prefs_);
+    profile_prefs_->SetString(prefs::kLiveCaptionLanguageCode,
+                              speech::GetDefaultLiveCaptionLanguage(
+                                  application_locale_, profile_prefs_));
+  }
+}
+
 }  // namespace captions
diff --git a/components/live_caption/live_caption_controller.h b/components/live_caption/live_caption_controller.h
index c6f3989..b2ee501 100644
--- a/components/live_caption/live_caption_controller.h
+++ b/components/live_caption/live_caption_controller.h
@@ -105,6 +105,8 @@
   void CreateUI();
   void DestroyUI();
 
+  void MaybeSetLiveCaptionLanguage();
+
   raw_ptr<PrefService> profile_prefs_;
   raw_ptr<PrefService> global_prefs_;
   raw_ptr<content::BrowserContext> browser_context_;
diff --git a/components/media_message_center/media_notification_view_impl.cc b/components/media_message_center/media_notification_view_impl.cc
index 37b856e..514ea82 100644
--- a/components/media_message_center/media_notification_view_impl.cc
+++ b/components/media_message_center/media_notification_view_impl.cc
@@ -129,6 +129,13 @@
   auto main_row = std::make_unique<views::View>();
   main_row_ = AddChildView(std::move(main_row));
 
+  // TODO(crbug.com/40232718): `main_row_` sets the flex property in
+  // `UpdateViewForExpandedState`, which means it will always satisfy the
+  // constraints passed in during the CalculatePreferredSize phase. So we set it
+  // here to not require constraints. If possible, consider removing the
+  // following flex property
+  main_row_->SetLayoutManagerUseConstrainedSpace(false);
+
   // |title_artist_row_| contains the title and artist labels.
   auto title_artist_row = std::make_unique<views::View>();
   title_artist_row_layout_ =
diff --git a/components/omnibox/DEPS b/components/omnibox/DEPS
index b2a2f53..4c863f8 100644
--- a/components/omnibox/DEPS
+++ b/components/omnibox/DEPS
@@ -1,3 +1,5 @@
 include_rules = [
+  "+ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java",
+  "+ui/android/java/src/org/chromium/ui/base/DeviceInput.java",
   "+ui/base",
 ]
\ No newline at end of file
diff --git a/components/omnibox/browser/shortcuts_backend.cc b/components/omnibox/browser/shortcuts_backend.cc
index 15335448..4f6218be 100644
--- a/components/omnibox/browser/shortcuts_backend.cc
+++ b/components/omnibox/browser/shortcuts_backend.cc
@@ -12,6 +12,7 @@
 #include <string>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/i18n/case_conversion.h"
 #include "base/metrics/histogram_macros.h"
@@ -22,6 +23,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/uuid.h"
+#include "components/history/core/browser/history_backend.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
@@ -30,9 +32,17 @@
 #include "components/omnibox/browser/in_memory_url_index_types.h"
 #include "components/omnibox/browser/shortcuts_database.h"
 #include "components/omnibox/browser/tailored_word_break_iterator.h"
+#include "components/omnibox/common/omnibox_features.h"
 
 namespace {
 
+// The amount of time, in minutes, to wait after initialization before
+// attempting to expire old shortcuts. Used to avoid contention with other work
+// performed on profile loading and to outwait the 30 second delay before
+// `ExpireHistoryBackend` starts history deletions, in case the initialization
+// of that and `ShortcutsBackend` happen around the same time.
+const int kInitialExpirationDelayMinutes = 2;
+
 // Takes Match classification vector and removes all matched positions,
 // compacting repetitions if necessary.
 std::string StripMatchMarkers(const ACMatchClassifications& matches) {
@@ -443,16 +453,31 @@
 void ShortcutsBackend::InitInternal() {
   DCHECK(current_state_ == INITIALIZING);
   db_->Init();
+
   ShortcutsDatabase::GuidToShortcutMap shortcuts;
   db_->LoadShortcuts(&shortcuts);
+
   temp_shortcuts_map_ = std::make_unique<ShortcutMap>();
   temp_guid_map_ = std::make_unique<GuidMap>();
+  const base::Time now(base::Time::Now());
+  int num_old_shortcuts = 0;
   for (ShortcutsDatabase::GuidToShortcutMap::const_iterator it(
            shortcuts.begin());
        it != shortcuts.end(); ++it) {
     (*temp_guid_map_)[it->first] = temp_shortcuts_map_->insert(
         std::make_pair(base::i18n::ToLower(it->second.text), it->second));
+    if (now - it->second.last_access_time >
+        base::Days(history::HistoryBackend::kExpireDaysThreshold)) {
+      num_old_shortcuts++;
+    }
   }
+
+  // Record database statistics.
+  UMA_HISTOGRAM_COUNTS_10000("ShortcutsProvider.DatabaseSize",
+                             temp_shortcuts_map_->size());
+  UMA_HISTOGRAM_COUNTS_10000("ShortcutsProvider.DatabaseSize.OldEntries",
+                             num_old_shortcuts);
+
   main_runner_->PostTask(
       FROM_HERE, base::BindOnce(&ShortcutsBackend::InitCompleted, this));
 }
@@ -462,14 +487,19 @@
   temp_shortcuts_map_->swap(shortcuts_map_);
   temp_shortcuts_map_.reset(nullptr);
   temp_guid_map_.reset(nullptr);
-  // This histogram is expired but the code was intentionally left behind so
-  // it can be easily re-enabled when launching Shortcuts provider on Android
-  // or iOS.
-  UMA_HISTOGRAM_COUNTS_10000("ShortcutsProvider.DatabaseSize",
-                             shortcuts_map_.size());
+
   current_state_ = INITIALIZED;
   for (ShortcutsBackendObserver& observer : observer_list_)
     observer.OnShortcutsLoaded();
+
+  if (base::FeatureList::IsEnabled(omnibox::kOmniboxDeleteOldShortcuts)) {
+    main_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(
+            base::IgnoreResult(&ShortcutsBackend::DeleteOldShortcuts),
+            weak_factory_.GetWeakPtr()),
+        base::Minutes(kInitialExpirationDelayMinutes));
+  }
 }
 
 bool ShortcutsBackend::AddShortcut(
@@ -567,3 +597,17 @@
                  base::IgnoreResult(&ShortcutsDatabase::DeleteAllShortcuts),
                  db_.get()));
 }
+
+bool ShortcutsBackend::DeleteOldShortcuts() {
+  ShortcutsDatabase::ShortcutIDs shortcut_ids;
+  const base::Time now(base::Time::Now());
+  for (const auto& guid_pair : guid_map_) {
+    if (now - guid_pair.second->second.last_access_time >
+        base::Days(history::HistoryBackend::kExpireDaysThreshold)) {
+      shortcut_ids.push_back(guid_pair.first);
+    }
+  }
+  UMA_HISTOGRAM_COUNTS_10000("ShortcutsProvider.OldEntryDeletions",
+                             shortcut_ids.size());
+  return DeleteShortcutsWithIDs(shortcut_ids);
+}
diff --git a/components/omnibox/browser/shortcuts_backend.h b/components/omnibox/browser/shortcuts_backend.h
index 320911b..9691eab 100644
--- a/components/omnibox/browser/shortcuts_backend.h
+++ b/components/omnibox/browser/shortcuts_backend.h
@@ -14,6 +14,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/scoped_observation.h"
 #include "base/synchronization/lock.h"
@@ -164,6 +165,18 @@
   // Deletes all of the shortcuts.
   bool DeleteAllShortcuts();
 
+  // Deletes all shortcuts whose `last_access_time` is older than the threshold
+  // defined by HistoryBackend.
+  //
+  // This is called once on initialization after a short delay in order to
+  // remove any shortcuts that have not been removed by calls to
+  // `OnHistoryDeletions()`. That method is called from `HistoryService`, which
+  // can be initialized and running before `ShortcutsBackend` is created since
+  // the former is created at browser startup but the latter is not created
+  // until a browser window has been created, leading to the initialization of
+  // the autocomplete system.
+  bool DeleteOldShortcuts();
+
   raw_ptr<TemplateURLService> template_url_service_;
   std::unique_ptr<SearchTermsData> search_terms_data_;
 
@@ -190,6 +203,8 @@
 
   // For some unit-test only.
   bool no_db_access_;
+
+  base::WeakPtrFactory<ShortcutsBackend> weak_factory_{this};
 };
 
 #endif  // COMPONENTS_OMNIBOX_BROWSER_SHORTCUTS_BACKEND_H_
diff --git a/components/omnibox/browser/shortcuts_backend_unittest.cc b/components/omnibox/browser/shortcuts_backend_unittest.cc
index 9499b157..2ede650 100644
--- a/components/omnibox/browser/shortcuts_backend_unittest.cc
+++ b/components/omnibox/browser/shortcuts_backend_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/test/history_service_test_util.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
@@ -74,6 +75,7 @@
   bool DeleteShortcutsWithURL(const GURL& url);
   bool DeleteShortcutsWithIDs(
       const ShortcutsDatabase::ShortcutIDs& deleted_ids);
+  bool DeleteOldShortcuts();
   bool ShortcutExists(const std::u16string& terms) const;
   std::vector<std::u16string> ShortcutsMapTexts() const;
   void ClearShortcutsMap();
@@ -204,6 +206,10 @@
   return backend_->DeleteShortcutsWithIDs(deleted_ids);
 }
 
+bool ShortcutsBackendTest::DeleteOldShortcuts() {
+  return backend_->DeleteOldShortcuts();
+}
+
 bool ShortcutsBackendTest::ShortcutExists(const std::u16string& terms) const {
   return shortcuts_map().find(terms) != shortcuts_map().end();
 }
@@ -415,6 +421,57 @@
   ASSERT_EQ(0U, shortcuts_map().size());
 }
 
+TEST_F(ShortcutsBackendTest, DeleteOldShortcuts) {
+  InitBackend();
+
+  // Define shortcuts that are 1, 10, 100 and 1000 days old.
+  ShortcutsDatabase::Shortcut shortcut1(
+      "BD85DBA2-8C29-49F9-84AE-48E1E90880DF", u"google",
+      MatchCoreForTesting("http://www.google.com"),
+      base::Time::Now() - base::Days(1), 100);
+  EXPECT_TRUE(AddShortcut(shortcut1));
+
+  ShortcutsDatabase::Shortcut shortcut2(
+      "BD85DBA2-8C29-49F9-84AE-48E1E90880E0", u"yahoo",
+      MatchCoreForTesting("http://www.yahoo.com"),
+      base::Time::Now() - base::Days(10), 10);
+  EXPECT_TRUE(AddShortcut(shortcut2));
+
+  ShortcutsDatabase::Shortcut shortcut3(
+      "BD85DBA2-8C29-49F9-84AE-48E1E90880E1", u"baidu",
+      MatchCoreForTesting("http://www.baidu.com"),
+      base::Time::Now() - base::Days(100), 1000);
+  EXPECT_TRUE(AddShortcut(shortcut3));
+
+  ShortcutsDatabase::Shortcut shortcut4(
+      "BD85DBA2-8C29-49F9-84AE-48E1E90880E2", u"bing",
+      MatchCoreForTesting("http://www.bing.com"),
+      base::Time::Now() - base::Days(1000), 1);
+  EXPECT_TRUE(AddShortcut(shortcut4));
+
+  ASSERT_EQ(4U, shortcuts_map().size());
+  EXPECT_EQ(shortcut1.id, shortcuts_map().find(shortcut1.text)->second.id);
+  EXPECT_EQ(shortcut2.id, shortcuts_map().find(shortcut2.text)->second.id);
+  EXPECT_EQ(shortcut3.id, shortcuts_map().find(shortcut3.text)->second.id);
+  EXPECT_EQ(shortcut4.id, shortcuts_map().find(shortcut4.text)->second.id);
+
+  EXPECT_TRUE(DeleteOldShortcuts());
+
+  // After deleting old shortcuts, the two that are more than 90 days old should
+  // no longer be present.
+  ASSERT_EQ(2U, shortcuts_map().size());
+  const ShortcutsBackend::ShortcutMap::const_iterator shortcut1_iter(
+      shortcuts_map().find(shortcut1.text));
+  ASSERT_TRUE(shortcut1_iter != shortcuts_map().end());
+  EXPECT_EQ(shortcut1.id, shortcut1_iter->second.id);
+  const ShortcutsBackend::ShortcutMap::const_iterator shortcut2_iter(
+      shortcuts_map().find(shortcut2.text));
+  ASSERT_TRUE(shortcut2_iter != shortcuts_map().end());
+  EXPECT_EQ(shortcut2.id, shortcut2_iter->second.id);
+  EXPECT_EQ(0U, shortcuts_map().count(shortcut3.text));
+  EXPECT_EQ(0U, shortcuts_map().count(shortcut4.text));
+}
+
 TEST_F(ShortcutsBackendTest, AddOrUpdateShortcut_3CharShortening) {
   InitBackend();
 
diff --git a/components/omnibox/common/BUILD.gn b/components/omnibox/common/BUILD.gn
index aa4de46..08504d1 100644
--- a/components/omnibox/common/BUILD.gn
+++ b/components/omnibox/common/BUILD.gn
@@ -49,8 +49,23 @@
     deps = [
       "//base:base_cached_flags_java",
       "//base:base_java",
+      "//third_party/androidx:androidx_annotation_annotation_java",
       "//third_party/jni_zero:jni_zero_java",
       "//third_party/omnibox_proto:omnibox_proto_java",
+      "//ui/android:ui_java",
+    ]
+  }
+
+  robolectric_library("junit") {
+    sources = [ "android/java/src/org/chromium/components/omnibox/OmniboxFeaturesTest.java" ]
+    deps = [
+      "//base:base_cached_flags_java",
+      "//base:base_java_test_support",
+      "//base:base_junit_test_support",
+      "//components/omnibox/common:features_java",
+      "//third_party/androidx:androidx_test_runner_java",
+      "//third_party/junit",
+      "//ui/android:ui_java",
     ]
   }
 }
diff --git a/components/omnibox/common/android/java/src/org/chromium/components/omnibox/OmniboxFeatures.java b/components/omnibox/common/android/java/src/org/chromium/components/omnibox/OmniboxFeatures.java
index 6a1e13dd..fb62c83 100644
--- a/components/omnibox/common/android/java/src/org/chromium/components/omnibox/OmniboxFeatures.java
+++ b/components/omnibox/common/android/java/src/org/chromium/components/omnibox/OmniboxFeatures.java
@@ -4,6 +4,8 @@
 
 package org.chromium.components.omnibox;
 
+import androidx.annotation.VisibleForTesting;
+
 import org.chromium.base.BaseSwitches;
 import org.chromium.base.CommandLine;
 import org.chromium.base.ResettersForTesting;
@@ -12,6 +14,8 @@
 import org.chromium.base.cached_flags.CachedFieldTrialParameter;
 import org.chromium.base.cached_flags.CachedFlag;
 import org.chromium.base.cached_flags.IntCachedFieldTrialParameter;
+import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.base.DeviceInput;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -65,6 +69,11 @@
     public static final CachedFlag sElegantTextHeight =
             newFlag(OmniboxFeatureList.OMNIBOX_ELEGANT_TEXT_HEIGHT, false);
 
+    /** See {@link #shouldRetainOmniboxOnFocus()}. */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final CachedFlag sRetainOmniboxOnFocus =
+            newFlag(OmniboxFeatureList.RETAIN_OMNIBOX_ON_FOCUS, false);
+
     public static final BooleanCachedFieldTrialParameter sAnswerActionsShowAboveKeyboard =
             newBooleanParam(sOmniboxAnswerActions, "AnswerActionsShowAboveKeyboard", false);
 
@@ -89,6 +98,9 @@
                     "rich_autocomplete_minimum_characters",
                     Integer.MAX_VALUE);
 
+    /** See {@link #setShouldRetainOmniboxOnFocusForTesting(boolean)}. */
+    private static Boolean sShouldRetainOmniboxOnFocusForTesting;
+
     /**
      * Create an instance of a CachedFeatureFlag.
      *
@@ -231,4 +243,27 @@
                 && sRichInlineShowFullUrl.getValue()
                 && inputCount >= sRichInlineMinimumInputChars.getValue();
     }
+
+    /** Modifies the output of {@link #shouldRetainOmniboxOnFocus()} for testing. */
+    public static void setShouldRetainOmniboxOnFocusForTesting(Boolean shouldRetainOmniboxOnFocus) {
+        sShouldRetainOmniboxOnFocusForTesting = shouldRetainOmniboxOnFocus;
+        ResettersForTesting.register(() -> sShouldRetainOmniboxOnFocusForTesting = null);
+    }
+
+    /**
+     * @return Whether the contents of the omnibox should be retained on focus as opposed to being
+     *     cleared. When {@code true} and the omnibox contents are retained, focus events will also
+     *     result in the omnibox contents being fully selected so as to allow for easy replacement
+     *     by the user. Note that only large screen devices with an attached keyboard and precision
+     *     pointer will exhibit a change in behavior when the feature flag is enabled.
+     */
+    public static boolean shouldRetainOmniboxOnFocus() {
+        if (sShouldRetainOmniboxOnFocusForTesting != null) {
+            return sShouldRetainOmniboxOnFocusForTesting;
+        }
+        return sRetainOmniboxOnFocus.isEnabled()
+                && DeviceFormFactor.isTablet()
+                && DeviceInput.supportsAlphabeticKeyboard()
+                && DeviceInput.supportsPrecisionPointer();
+    }
 }
diff --git a/components/omnibox/common/android/java/src/org/chromium/components/omnibox/OmniboxFeaturesTest.java b/components/omnibox/common/android/java/src/org/chromium/components/omnibox/OmniboxFeaturesTest.java
new file mode 100644
index 0000000..45d7f1d
--- /dev/null
+++ b/components/omnibox/common/android/java/src/org/chromium/components/omnibox/OmniboxFeaturesTest.java
@@ -0,0 +1,113 @@
+// 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.components.omnibox;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.robolectric.ParameterizedRobolectricTestRunner;
+import org.robolectric.ParameterizedRobolectricTestRunner.Parameter;
+import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
+
+import org.chromium.base.test.BaseRobolectricTestRule;
+import org.chromium.base.test.util.Features.DisableFeatures;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.base.DeviceInput;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/** Tests for {@link OmniboxFeatures}. */
+@RunWith(Enclosed.class)
+public class OmniboxFeaturesTest {
+
+    /** Parameterized tests for {@link OmniboxFeatures#shouldRetainOmniboxOnFocus()}. */
+    @RunWith(ParameterizedRobolectricTestRunner.class)
+    public static class ShouldRetainOmniboxOnFocusTest {
+
+        /** Returns all possible combinations for test parameterization. */
+        @Parameters(name = "{0}, {1}, {2}")
+        public static Collection<Object[]> data() {
+            Collection<Object[]> data = new ArrayList<>();
+            for (boolean deviceFormFactorIsTablet : List.of(true, false)) {
+                for (boolean deviceInputSupportsAlphabeticKeyboard : List.of(true, false)) {
+                    for (boolean deviceInputSupportsPrecisionPointer : List.of(true, false)) {
+                        data.add(
+                                new Object[] {
+                                    deviceFormFactorIsTablet,
+                                    deviceInputSupportsAlphabeticKeyboard,
+                                    deviceInputSupportsPrecisionPointer
+                                });
+                    }
+                }
+            }
+            return data;
+        }
+
+        @Rule(order = -2)
+        public BaseRobolectricTestRule mBaseRule = new BaseRobolectricTestRule();
+
+        /** Whether the device form factor is a tablet. */
+        @Parameter(0)
+        public boolean mDeviceFormFactorIsTablet;
+
+        /** Whether device input supports an alphabetic keyboard. */
+        @Parameter(1)
+        public boolean mDeviceInputSupportsAlphabeticKeyboard;
+
+        /** Whether device input supports a precision pointer. */
+        @Parameter(2)
+        public boolean mDeviceInputSupportsPrecisionPointer;
+
+        @Before
+        public void setUp() {
+            DeviceFormFactor.setIsTabletForTesting(mDeviceFormFactorIsTablet);
+            DeviceInput.setSupportsAlphabeticKeyboardForTesting(
+                    mDeviceInputSupportsAlphabeticKeyboard);
+            DeviceInput.setSupportsPrecisionPointerForTesting(mDeviceInputSupportsPrecisionPointer);
+        }
+
+        @Test
+        @SmallTest
+        public void testShouldRetainOmniboxOnFocus_withFeatureInDefaultState() {
+            testShouldRetainOmniboxOnFocus(/* expectFeatureEnabled= */ false);
+        }
+
+        @Test
+        @SmallTest
+        @DisableFeatures(OmniboxFeatureList.RETAIN_OMNIBOX_ON_FOCUS)
+        public void testShouldRetainOmniboxOnFocus_withFeatureExplicitlyDisabled() {
+            testShouldRetainOmniboxOnFocus(/* expectFeatureEnabled= */ false);
+        }
+
+        @Test
+        @SmallTest
+        @EnableFeatures(OmniboxFeatureList.RETAIN_OMNIBOX_ON_FOCUS)
+        public void testShouldRetainOmniboxOnFocus_withFeatureExplicitlyEnabled() {
+            testShouldRetainOmniboxOnFocus(/* expectFeatureEnabled= */ true);
+        }
+
+        private void testShouldRetainOmniboxOnFocus(boolean expectFeatureEnabled) {
+            boolean featureEnabled = OmniboxFeatures.sRetainOmniboxOnFocus.isEnabled();
+            Assert.assertEquals(expectFeatureEnabled, featureEnabled);
+
+            boolean expectRetainOmniboxOnFocus =
+                    featureEnabled
+                            && mDeviceFormFactorIsTablet
+                            && mDeviceInputSupportsAlphabeticKeyboard
+                            && mDeviceInputSupportsPrecisionPointer;
+
+            Assert.assertEquals(
+                    expectRetainOmniboxOnFocus, OmniboxFeatures.shouldRetainOmniboxOnFocus());
+        }
+    }
+}
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 139b4060..7744067 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -538,11 +538,6 @@
              "ReportApplicationLanguageInSearchRequest",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Enables storing successful query/match in the shortcut database On Android.
-BASE_FEATURE(kOmniboxShortcutsAndroid,
-             "OmniboxShortcutsAndroid",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enable asynchronous Omnibox/Suggest view inflation.
 BASE_FEATURE(kOmniboxAsyncViewInflation,
              "OmniboxAsyncViewInflation",
@@ -553,6 +548,16 @@
              "UseFusedLocationProvider",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Enables storing successful query/match in the shortcut database On Android.
+BASE_FEATURE(kOmniboxShortcutsAndroid,
+             "OmniboxShortcutsAndroid",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+// Enables deletion of old shortcuts on profile load.
+BASE_FEATURE(kOmniboxDeleteOldShortcuts,
+             "OmniboxDeleteOldShortcuts",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 #if BUILDFLAG(IS_ANDROID)
 // Enable the Elegant Text Height attribute on the UrlBar.
 // This attribute increases line height by up to 60% to accommodate certain
@@ -565,6 +570,16 @@
              "OmniboxAblateVisibleNetworks",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Whether the contents of the omnibox should be retained on focus as opposed to
+// being cleared. When this feature flag is enabled and the omnibox contents are
+// retained, focus events will also result in the omnibox contents being fully
+// selected so as to allow for easy replacement by the user. Note that even with
+// this feature flag enabled, only large screen devices with an attached
+// keyboard and precision pointer will exhibit a change in behavior.
+BASE_FEATURE(kRetainOmniboxOnFocus,
+             "RetainOmniboxOnFocus",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 namespace android {
 static jlong JNI_OmniboxFeatureMap_GetNativeMap(JNIEnv* env) {
   static base::NoDestructor<base::android::FeatureMap> kFeatureMap(
@@ -577,6 +592,7 @@
           &kUseFusedLocationProvider,
           &kOmniboxElegantTextHeight,
           &kOmniboxAblateVisibleNetworks,
+          &kRetainOmniboxOnFocus,
       }});
 
   return reinterpret_cast<jlong>(kFeatureMap.get());
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index e8ead71..5891b1d2 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -157,10 +157,17 @@
 BASE_DECLARE_FEATURE(kDropUnrecognizedTemplateUrlParameters);
 BASE_DECLARE_FEATURE(kReportApplicationLanguageInSearchRequest);
 
-BASE_DECLARE_FEATURE(kOmniboxShortcutsAndroid);
 BASE_DECLARE_FEATURE(kOmniboxAsyncViewInflation);
 BASE_DECLARE_FEATURE(kUseFusedLocationProvider);
 
+#if BUILDFLAG(IS_ANDROID)
+BASE_DECLARE_FEATURE(kRetainOmniboxOnFocus);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+// `ShortcutsProvider` features.
+BASE_DECLARE_FEATURE(kOmniboxShortcutsAndroid);
+BASE_DECLARE_FEATURE(kOmniboxDeleteOldShortcuts);
+
 }  // namespace omnibox
 
 #endif  // COMPONENTS_OMNIBOX_COMMON_OMNIBOX_FEATURES_H_
diff --git a/components/optimization_guide/core/model_execution/session_impl.cc b/components/optimization_guide/core/model_execution/session_impl.cc
index bf86697..3cc7f206 100644
--- a/components/optimization_guide/core/model_execution/session_impl.cc
+++ b/components/optimization_guide/core/model_execution/session_impl.cc
@@ -993,7 +993,9 @@
 void SessionImpl::GetSizeInTokens(
     const std::string& text,
     OptimizationGuideModelSizeInTokenCallback callback) {
-  GetOrCreateSession().GetSizeInTokens(text, std::move(callback));
+  auto input = on_device_model::mojom::Input::New();
+  input->pieces.push_back(text);
+  GetOrCreateSession().GetSizeInTokens(std::move(input), std::move(callback));
 }
 
 const SamplingParams SessionImpl::GetSamplingParams() const {
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index e3a75e7..ffacd3b 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit e3a75e7f401aaa2cf290716560fd066072fb2772
+Subproject commit ffacd3be35c88f3600039204210e14e31d1a04cc
diff --git a/components/os_crypt/async/browser/dpapi_key_provider_unittest.cc b/components/os_crypt/async/browser/dpapi_key_provider_unittest.cc
index bcdbdff..1b6a982 100644
--- a/components/os_crypt/async/browser/dpapi_key_provider_unittest.cc
+++ b/components/os_crypt/async/browser/dpapi_key_provider_unittest.cc
@@ -29,8 +29,10 @@
 class DPAPIKeyProviderTestBase : public ::testing::Test {
  protected:
   void TearDown() override {
-    histograms_.ExpectBucketCount("OSCrypt.DPAPIProvider.Status",
-                                  expected_histogram_, 1);
+    if (expected_histogram_) {
+      histograms_.ExpectBucketCount("OSCrypt.DPAPIProvider.Status",
+                                    *expected_histogram_, 1);
+    }
   }
 
   Encryptor GetInstanceSync(
@@ -59,7 +61,7 @@
   }
 
   TestingPrefServiceSimple prefs_;
-  DPAPIKeyProvider::KeyStatus expected_histogram_ =
+  std::optional<DPAPIKeyProvider::KeyStatus> expected_histogram_ =
       DPAPIKeyProvider::KeyStatus::kSuccess;
 
  private:
@@ -121,6 +123,10 @@
 
 // Very small Key Provider that provides a random key.
 class RandomKeyProvider : public KeyProvider {
+ public:
+  RandomKeyProvider(bool use_for_encryption = true)
+      : use_for_encryption_(use_for_encryption) {}
+
  private:
   void GetKey(KeyCallback callback) final {
     std::vector<uint8_t> key(Encryptor::Key::kAES256GCMKeySize);
@@ -129,8 +135,10 @@
                             Encryptor::Key(key, mojom::Algorithm::kAES256GCM));
   }
 
-  bool UseForEncryption() final { return true; }
+  bool UseForEncryption() final { return use_for_encryption_; }
   bool IsCompatibleWithOsCryptSync() final { return false; }
+
+  const bool use_for_encryption_;
 };
 
 TEST_F(DPAPIKeyProviderTest, EncryptWithOptions) {
@@ -205,6 +213,114 @@
     }
   }
 }
+
+TEST_F(DPAPIKeyProviderTest, ShouldReencrypt) {
+  std::string ciphertext;
+  // This test re-initializes the DPAPIKeyProvider a few times, so ignore
+  // histogram results. These histograms are tested elsewhere.
+  expected_histogram_ = std::nullopt;
+
+  ASSERT_TRUE(OSCrypt::EncryptString("oscryptsecrets", &ciphertext));
+
+  {
+    auto encryptor = GetInstanceWithDPAPI();
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    // This should not require re-encryption because the only provider
+    // registered, the DPAPI one, successfully decrypted the data, even though
+    // it was originally encrypted by OSCrypt Sync.
+    EXPECT_FALSE(flags.should_reencrypt);
+  }
+
+  {
+    std::vector<std::pair<size_t, std::unique_ptr<KeyProvider>>> providers;
+    providers.emplace_back(std::make_pair(
+        /*precedence=*/15u, std::make_unique<RandomKeyProvider>()));
+
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    // This decryption used OSCrypt sync fallback, but there was a key provider
+    // registered for encryption, so the data should be re-encrypted for the new
+    // key provider.
+    EXPECT_TRUE(flags.should_reencrypt);
+  }
+
+  {
+    std::vector<std::pair<size_t, std::unique_ptr<KeyProvider>>> providers;
+    providers.emplace_back(std::make_pair(
+        /*precedence=*/15u,
+        std::make_unique<RandomKeyProvider>(/*use_for_encryption=*/false)));
+
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    // This decryption used OSCrypt sync fallback, and the only key provider
+    // registered did not say it should encrypt, so this should not re-encrypt.
+    EXPECT_FALSE(flags.should_reencrypt);
+  }
+
+  {
+    std::vector<std::pair<size_t, std::unique_ptr<KeyProvider>>> providers;
+    providers.emplace_back(std::make_pair(
+        /*precedence=*/10u, std::make_unique<DPAPIKeyProvider>(&prefs_)));
+    providers.emplace_back(std::make_pair(
+        /*precedence=*/15u, std::make_unique<RandomKeyProvider>()));
+
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    // This decryption used the DPAPI key provider, but there is also a
+    // RandomKeyProvider registered so data should be re-encrypted.
+    EXPECT_TRUE(flags.should_reencrypt);
+  }
+
+  {
+    std::vector<std::pair<size_t, std::unique_ptr<KeyProvider>>> providers;
+    providers.emplace_back(std::make_pair(
+        /*precedence=*/10u, std::make_unique<DPAPIKeyProvider>(&prefs_)));
+    providers.emplace_back(std::make_pair(
+        /*precedence=*/15u,
+        std::make_unique<RandomKeyProvider>(/*use_for_encryption=*/false)));
+
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    // This decryption used the DPAPI key provider. The RandomKeyProvider is not
+    // to be used for encryption, so the data can remain encrypted OSCrypt Sync
+    // compatible.
+    EXPECT_FALSE(flags.should_reencrypt);
+  }
+
+  {
+    std::vector<std::pair<size_t, std::unique_ptr<KeyProvider>>> providers;
+    providers.emplace_back(std::make_pair(
+        /*precedence=*/10u, std::make_unique<DPAPIKeyProvider>(&prefs_)));
+    providers.emplace_back(std::make_pair(
+        /*precedence=*/5u, std::make_unique<RandomKeyProvider>()));
+
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    // In this test, both key providers are enabled for encryption but the
+    // RandomKeyProvider is a lower precedence than the DPAPI key provider, so
+    // will not be the default for encryption, therefore since the data is
+    // already encrypted with DPAPI, a re-encryption should not be requested.
+    EXPECT_FALSE(flags.should_reencrypt);
+  }
+}
+
 // Only test a few scenarios here, just to verify the error histogram is always
 // logged.
 TEST_F(DPAPIKeyProviderTest, OSCryptNotInit) {
diff --git a/components/os_crypt/async/browser/os_crypt_async_unittest.cc b/components/os_crypt/async/browser/os_crypt_async_unittest.cc
index f537e381..e3ef9be0 100644
--- a/components/os_crypt/async/browser/os_crypt_async_unittest.cc
+++ b/components/os_crypt/async/browser/os_crypt_async_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/os_crypt/sync/os_crypt.h"
 #include "components/os_crypt/sync/os_crypt_mocker.h"
 #include "crypto/hkdf.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_LINUX)
@@ -317,58 +318,6 @@
     EXPECT_TRUE(
         MaybeVerifyFailedDecryptOperation(failing_plaintext, *test_ciphertext));
   }
-  // Test also that if there are multiple key providers with
-  // compatible_with_os_crypt_sync then the highest precedence is picked.
-  {
-    ProviderList providers;
-    providers.emplace_back(/*precedence=*/10u,
-                           std::make_unique<TestKeyProvider>(
-                               kTESTProviderName, /*use_for_encryption=*/true,
-                               /*compatible_with_os_crypt_sync=*/true));
-    providers.emplace_back(/*precedence=*/8u,
-                           std::make_unique<TestKeyProvider>(
-                               "FOO", /*use_for_encryption=*/true,
-                               /*compatible_with_os_crypt_sync=*/false));
-    providers.emplace_back(/*precedence=*/5u,
-                           std::make_unique<TestKeyProvider>(
-                               "BAR", /*use_for_encryption=*/true,
-                               /*compatible_with_os_crypt_sync=*/true));
-    OSCryptAsync factory(std::move(providers));
-    Encryptor encryptor =
-        GetInstanceSync(factory, Encryptor::Option::kEncryptSyncCompat);
-    const auto ciphertext = encryptor.EncryptString("secrets");
-    ASSERT_TRUE(ciphertext);
-    // Should be encrypted with TEST - it's the highest precedence provider
-    // that indicates it's compatible with OSCrypt sync.
-    EXPECT_TRUE(std::equal(kTESTProviderName.cbegin(), kTESTProviderName.cend(),
-                           ciphertext->cbegin()));
-  }
-  // Just in case, test that order doesn't matter here, although this is also
-  // tested elsewhere.
-  {
-    ProviderList providers;
-    providers.emplace_back(/*precedence=*/5u,
-                           std::make_unique<TestKeyProvider>(
-                               "BAR", /*use_for_encryption=*/true,
-                               /*compatible_with_os_crypt_sync=*/true));
-    providers.emplace_back(/*precedence=*/8u,
-                           std::make_unique<TestKeyProvider>(
-                               "FOO", /*use_for_encryption=*/true,
-                               /*compatible_with_os_crypt_sync=*/false));
-    providers.emplace_back(/*precedence=*/10u,
-                           std::make_unique<TestKeyProvider>(
-                               kTESTProviderName, /*use_for_encryption=*/true,
-                               /*compatible_with_os_crypt_sync=*/true));
-    OSCryptAsync factory(std::move(providers));
-    Encryptor encryptor =
-        GetInstanceSync(factory, Encryptor::Option::kEncryptSyncCompat);
-    const auto ciphertext = encryptor.EncryptString("secrets");
-    ASSERT_TRUE(ciphertext);
-    // Should be encrypted with TEST - it's the highest precedence provider
-    // that indicates it's compatible with OSCrypt sync.
-    EXPECT_TRUE(std::equal(kTESTProviderName.cbegin(), kTESTProviderName.cend(),
-                           ciphertext->cbegin()));
-  }
 }
 
 class SlowTestKeyProvider : public TestKeyProvider {
@@ -557,6 +506,91 @@
   }
 }
 
+TEST_F(OSCryptAsyncTest, ShouldReencrypt) {
+  std::string ciphertext;
+  {
+    ProviderList providers;
+    providers.emplace_back(
+        /*precedence=*/5u,
+        std::make_unique<TestKeyProvider>("BAR", /*use_for_encryption=*/true));
+    providers.emplace_back(
+        /*precedence=*/8u,
+        std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/true));
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    ASSERT_TRUE(encryptor.EncryptString("secrets", &ciphertext));
+    // FOO should be used, as it's the higher precedence.
+    EXPECT_THAT(base::make_span(ciphertext).first(3u),
+                ::testing::ElementsAreArray(base::span_from_cstring("FOO")));
+    std::string plaintext;
+    Encryptor::DecryptFlags flags;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    EXPECT_EQ(plaintext, "secrets");
+    EXPECT_FALSE(flags.should_reencrypt);
+  }
+
+  {
+    ProviderList providers;
+    providers.emplace_back(
+        /*precedence=*/5u,
+        std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/true));
+    providers.emplace_back(
+        /*precedence=*/8u,
+        std::make_unique<TestKeyProvider>("BAR", /*use_for_encryption=*/true));
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    EXPECT_EQ(plaintext, "secrets");
+    EXPECT_TRUE(flags.should_reencrypt);
+  }
+}
+
+TEST_F(OSCryptAsyncTestWithOSCrypt, OSCryptShouldReencrypt) {
+  std::string ciphertext;
+  ASSERT_TRUE(OSCrypt::EncryptString("secrets", &ciphertext));
+
+  {
+    ProviderList providers;
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    std::string plaintext;
+    Encryptor::DecryptFlags flags;
+    // This encryptor has no providers, so falls back to OSCrypt Sync.
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    EXPECT_EQ(plaintext, "secrets");
+    EXPECT_FALSE(flags.should_reencrypt);
+  }
+  {
+    ProviderList providers;
+    providers.emplace_back(
+        /*precedence=*/5u,
+        std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/true));
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    EXPECT_EQ(plaintext, "secrets");
+    EXPECT_TRUE(flags.should_reencrypt);
+  }
+  {
+    ProviderList providers;
+    // Specify not to encrypt data with this provider.
+    providers.emplace_back(
+        /*precedence=*/5u,
+        std::make_unique<TestKeyProvider>("FOO", /*use_for_encryption=*/false));
+    OSCryptAsync factory(std::move(providers));
+    Encryptor encryptor = GetInstanceSync(factory);
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    EXPECT_EQ(plaintext, "secrets");
+    EXPECT_FALSE(flags.should_reencrypt);
+  }
+}
+
 // Test also that if no key providers have OSCrypt sync compatibility then
 // encryption simply falls back to OSCrypt, and that OSCrypt can decrypt it
 // fine.
@@ -645,6 +679,24 @@
       "Tags must not overlap.");
 }
 
+TEST_F(OSCryptAsyncDeathTest, TwoOsCryptCompatibleKeyProviders) {
+  ProviderList providers;
+  providers.emplace_back(
+      /*precedence=*/10u, std::make_unique<TestKeyProvider>(
+                              "ABC", /*use_for_encryption=*/true,
+                              /*compatible_with_os_crypt_sync=*/true));
+  providers.emplace_back(
+      /*precedence=*/15u, std::make_unique<TestKeyProvider>(
+                              "DEF", /*use_for_encryption=*/true,
+                              /*compatible_with_os_crypt_sync=*/true));
+  EXPECT_DCHECK_DEATH_WITH(
+      {
+        OSCryptAsync factory(std::move(providers));
+        std::ignore = GetInstanceSync(factory);
+      },
+      "Cannot have more than one key marked OSCrypt sync compatible.");
+}
+
 TEST_F(OSCryptAsyncTest, NoCrashWithLongNames) {
   ProviderList providers;
   providers.emplace_back(
diff --git a/components/os_crypt/async/browser/test_utils.cc b/components/os_crypt/async/browser/test_utils.cc
index 99e6941..03b9fd1 100644
--- a/components/os_crypt/async/browser/test_utils.cc
+++ b/components/os_crypt/async/browser/test_utils.cc
@@ -45,20 +45,26 @@
 
   static Encryptor GetTestEncryptorForTesting() {
     Encryptor::KeyRing keys;
-    std::vector<uint8_t> key_data(Encryptor::Key::kAES256GCMKeySize);
-    crypto::RandBytes(key_data);
-    Encryptor::Key key(key_data, mojom::Algorithm::kAES256GCM);
-    // The test key used here indicates it is compatible with OSCrypt Sync
-    // because otherwise tests that ask for instances with the
-    // kEncryptSyncCompat option would fall back to OSCrypt Sync, and this
-    // requires the OSCrypt mocker to be installed, which should not be needed
-    // in tests and code ported to OSCrypt Async.
+    keys.emplace("k1", Encryptor::Key(crypto::RandBytesAsVector(
+                                          Encryptor::Key::kAES256GCMKeySize),
+                                      mojom::Algorithm::kAES256GCM));
+    Encryptor::Key key(
+        crypto::RandBytesAsVector(Encryptor::Key::kAES256GCMKeySize),
+        mojom::Algorithm::kAES256GCM);
+    // This test keyring has a second key that is OS Crypt Sync compatible.
+    // When a test requests an Encryptor that is OS Crypt Sync compatible, the
+    // k2 key will be picked, instead of the default k1 key. This allows
+    // testing of key upgrade scenarios.
     key.is_os_crypt_sync_compatible_ = true;
-    keys.emplace("_", std::move(key));
-    Encryptor encryptor(std::move(keys), "_");
+    keys.emplace("k2", std::move(key));
+    Encryptor encryptor(std::move(keys), "k1");
     return encryptor;
   }
 
+  static Encryptor CloneEncryptorForTesting(Encryptor::Option option) {
+    return GetTestEncryptorForTesting().Clone(option);
+  }
+
  private:
   Encryptor encryptor_;
   const bool is_sync_for_unittests_;
@@ -69,8 +75,8 @@
   return std::make_unique<TestOSCryptAsync>(is_sync_for_unittests);
 }
 
-Encryptor GetTestEncryptorForTesting() {
-  return TestOSCryptAsync::GetTestEncryptorForTesting();
+Encryptor GetTestEncryptorForTesting(Encryptor::Option option) {
+  return TestOSCryptAsync::CloneEncryptorForTesting(option);
 }
 
 }  // namespace os_crypt_async
diff --git a/components/os_crypt/async/browser/test_utils.h b/components/os_crypt/async/browser/test_utils.h
index 5e0f426c6..090e0aaa 100644
--- a/components/os_crypt/async/browser/test_utils.h
+++ b/components/os_crypt/async/browser/test_utils.h
@@ -20,8 +20,10 @@
 // Obtain a test Encryptor. This Encryptor will perform encryption using a
 // random key. The key for test Encryptors is different each time this function
 // is called, and different from the ones vended from the test OSCryptAsync
-// above.
-Encryptor GetTestEncryptorForTesting();
+// above. An `option` can be specified in the same way as calling `GetInstance`
+// on `OSCryptAsync`.
+Encryptor GetTestEncryptorForTesting(
+    Encryptor::Option option = Encryptor::Option::kNone);
 
 }  // namespace os_crypt_async
 
diff --git a/components/os_crypt/async/common/encryptor.cc b/components/os_crypt/async/common/encryptor.cc
index fe58513c..a041e3e 100644
--- a/components/os_crypt/async/common/encryptor.cc
+++ b/components/os_crypt/async/common/encryptor.cc
@@ -93,7 +93,18 @@
 
 Encryptor::Encryptor(KeyRing keys, const std::string& provider_for_encryption)
     : keys_(std::move(keys)),
-      provider_for_encryption_(provider_for_encryption) {}
+      provider_for_encryption_(provider_for_encryption) {
+  // It is not permitted to have multiple keys that mark themselves as OSCrypt
+  // sync compatible.
+  bool already_found_os_crypt_compatible = false;
+  for (const auto& key : keys_) {
+    if (key.second.is_os_crypt_sync_compatible_) {
+      CHECK(!already_found_os_crypt_compatible)
+          << "Cannot have more than one key marked OSCrypt sync compatible.";
+      already_found_os_crypt_compatible = true;
+    }
+  }
+}
 Encryptor::~Encryptor() = default;
 
 std::vector<uint8_t> Encryptor::Key::Encrypt(
@@ -190,8 +201,10 @@
 }
 
 bool Encryptor::DecryptString(const std::string& ciphertext,
-                              std::string* plaintext) const {
-  auto decrypted = DecryptData(base::as_bytes(base::make_span(ciphertext)));
+                              std::string* plaintext,
+                              DecryptFlags* flags) const {
+  auto decrypted =
+      DecryptData(base::as_bytes(base::make_span(ciphertext)), flags);
 
   if (!decrypted.has_value()) {
     return false;
@@ -231,7 +244,12 @@
 }
 
 std::optional<std::string> Encryptor::DecryptData(
-    base::span<const uint8_t> data) const {
+    base::span<const uint8_t> data,
+    DecryptFlags* flags) const {
+  if (flags) {
+    flags->should_reencrypt = false;
+  }
+
   if (data.empty()) {
     return std::string();
   }
@@ -246,6 +264,9 @@
       // The Key does the raw decrypt.
       auto plaintext = key.Decrypt(ciphertext);
       if (plaintext) {
+        if (flags) {
+          flags->should_reencrypt = provider != provider_for_encryption_;
+        }
         return std::string(plaintext->begin(), plaintext->end());
       }
     }
@@ -256,6 +277,33 @@
   std::string string_data(data.begin(), data.end());
   std::string plaintext;
   if (OSCrypt::DecryptString(string_data, &plaintext)) {
+    // If OSCrypt is using os_crypt_posix.cc and it's passed invalid data to
+    // decrypt, it simply returns the data. This is a quirk of
+    // os_crypt_posix.cc. In this case, it's not really possible to tell whether
+    // or not encryption worked or not, and certainly not advisable to recommend
+    // a re-encryption of this potentially invalid data.
+    // TODO(crbug.com/365712505): Remove this fallback.
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) &&         \
+        !(BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)) || \
+    BUILDFLAG(IS_FUCHSIA)
+    if (plaintext == string_data) {
+      return plaintext;
+    }
+#endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && !(BUILDFLAG(IS_LINUX)
+        // && !BUILDFLAG(IS_CASTOS)) || BUILDFLAG(IS_FUCHSIA)
+    if (!provider_for_encryption_.empty()) {
+      // Subtle: `is_os_crypt_sync_compatible_` is not transferred over mojo
+      // so will always be false in a non-browser process. However, this CHECK
+      // exists for correctness of provider registration so it does not matter
+      // if it never fires in these processes, as it will fire in browser
+      // first.
+      CHECK(!keys_.at(provider_for_encryption_).is_os_crypt_sync_compatible_)
+          << "A provider marked itself as OSCrypt Sync compatible but did not "
+             "decrypt OSCrypt Sync data.";
+      if (flags) {
+        flags->should_reencrypt = true;
+      }
+    }
     return plaintext;
   }
 
@@ -268,9 +316,10 @@
 }
 
 bool Encryptor::DecryptString16(const std::string& ciphertext,
-                                std::u16string* plaintext) const {
+                                std::u16string* plaintext,
+                                DecryptFlags* flags) const {
   std::string utf8;
-  if (!DecryptString(ciphertext, &utf8)) {
+  if (!DecryptString(ciphertext, &utf8, flags)) {
     return false;
   }
 
@@ -293,10 +342,8 @@
     case Option::kEncryptSyncCompat:
       for (const auto& [provider, key] : keyring) {
         if (key.is_os_crypt_sync_compatible_) {
-          // Keys are already sorted by precedence, so if multiple keys are
-          // compatible, the one with the highest precedence (later in the
-          // keyring) is picked.
           provider_for_encryption = provider;
+          break;
         }
       }
       break;
diff --git a/components/os_crypt/async/common/encryptor.h b/components/os_crypt/async/common/encryptor.h
index 388ab1a3..de2ad79 100644
--- a/components/os_crypt/async/common/encryptor.h
+++ b/components/os_crypt/async/common/encryptor.h
@@ -101,6 +101,16 @@
     kEncryptSyncCompat = 1,
   };
 
+  // Flags that can be set by the Encryptor during a Decrypt call. Pass to a
+  // Decrypt operation to obtain these flags.
+  struct DecryptFlags {
+    // Set by the Encryptor to indicate to the caller that the data that has
+    // just been returned from the Decrypt operation should be re-encrypted with
+    // a call to Encrypt, as the key has been rotated or a new key is available
+    // that provides a different security level.
+    bool should_reencrypt = false;
+  };
+
   using KeyRing = std::map</*tag=*/std::string, Key>;
 
   // Mojo uses this public constructor for serialization.
@@ -120,20 +130,25 @@
       const std::string& data) const;
 
   // Decrypt data previously encrypted using `EncryptData`. This can be called
-  // on any thread.
+  // on any thread. If a non-null `flags` is passed, then a set of flags is
+  // returned to indicate additional information for the caller. See
+  // `DecryptFlags` struct above.
   [[nodiscard]] std::optional<std::string> DecryptData(
-      base::span<const uint8_t> data) const;
+      base::span<const uint8_t> data,
+      DecryptFlags* flags = nullptr) const;
 
   // These four APIs are provided for backwards compatibility with OSCrypt. They
-  // just call the above functions.
+  // just call the above functions. For these functions, `flags` is optional.
   [[nodiscard]] bool EncryptString(const std::string& plaintext,
                                    std::string* ciphertext) const;
   [[nodiscard]] bool DecryptString(const std::string& ciphertext,
-                                   std::string* plaintext) const;
+                                   std::string* plaintext,
+                                   DecryptFlags* flags = nullptr) const;
   [[nodiscard]] bool EncryptString16(const std::u16string& plaintext,
                                      std::string* ciphertext) const;
   [[nodiscard]] bool DecryptString16(const std::string& ciphertext,
-                                     std::u16string* plaintext) const;
+                                     std::u16string* plaintext,
+                                     DecryptFlags* flags = nullptr) const;
 
   // Returns true if there is at least one key contained within the encryptor
   // that could be used for encryption, otherwise, it will return the value of
diff --git a/components/os_crypt/async/common/encryptor_unittest.cc b/components/os_crypt/async/common/encryptor_unittest.cc
index b421eb4..014261a 100644
--- a/components/os_crypt/async/common/encryptor_unittest.cc
+++ b/components/os_crypt/async/common/encryptor_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/os_crypt/async/common/encryptor.mojom.h"
 #include "components/os_crypt/sync/os_crypt.h"
 #include "components/os_crypt/sync/os_crypt_mocker.h"
+#include "crypto/hkdf.h"
 #include "crypto/random.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -132,6 +133,17 @@
     return key;
   }
 
+  static Encryptor::Key DeriveAES256TestKey(
+      std::string_view seed,
+      bool is_os_crypt_sync_compatible = false) {
+    auto key_data =
+        crypto::HkdfSha256(seed, {}, {}, Encryptor::Key::kAES256GCMKeySize);
+    Encryptor::Key key(base::as_byte_span(key_data),
+                       mojom::Algorithm::kAES256GCM);
+    key.is_os_crypt_sync_compatible_ = is_os_crypt_sync_compatible;
+    return key;
+  }
+
   // Simulate a 'locked' OSCrypt keychain on platforms that need it, which makes
   // OSCrypt::IsEncryptionAvailable return false, without hitting a CHECK on
   // Linux. Note this is different from using the full OSCryptMocker, because in
@@ -276,8 +288,9 @@
 
   auto ciphertext = encryptor.EncryptString(std::string());
   ASSERT_TRUE(ciphertext);
-
-  auto decrypted = encryptor.DecryptData(*ciphertext);
+  Encryptor::DecryptFlags flags;
+  auto decrypted = encryptor.DecryptData(*ciphertext, &flags);
+  ASSERT_FALSE(flags.should_reencrypt);
   ASSERT_TRUE(decrypted);
   EXPECT_TRUE(decrypted->empty());
 }
@@ -288,7 +301,9 @@
 TEST_P(EncryptorTest, DecryptEmpty) {
   const Encryptor encryptor = GetTestEncryptor();
 
-  auto plaintext = encryptor.DecryptData({});
+  Encryptor::DecryptFlags flags;
+  auto plaintext = encryptor.DecryptData({}, &flags);
+  ASSERT_FALSE(flags.should_reencrypt);
   ASSERT_TRUE(plaintext);
   EXPECT_TRUE(plaintext->empty());
 }
@@ -298,13 +313,22 @@
 TEST_P(EncryptorTest, DecryptInvalid) {
   const Encryptor encryptor = GetTestEncryptor();
 
-  std::vector<uint8_t> invalid_cipher(100);
-  for (size_t c = 0u; c < invalid_cipher.size(); c++) {
-    invalid_cipher[c] = c;
-  }
+  {
+    std::vector<uint8_t> invalid_cipher(100);
+    for (size_t c = 0u; c < invalid_cipher.size(); c++) {
+      invalid_cipher[c] = c;
+    }
 
-  auto plaintext = encryptor.DecryptData(invalid_cipher);
-  ASSERT_FALSE(plaintext);
+    Encryptor::DecryptFlags flags;
+    auto plaintext = encryptor.DecryptData(invalid_cipher, &flags);
+    ASSERT_FALSE(flags.should_reencrypt);
+    ASSERT_FALSE(plaintext);
+  }
+  {
+    std::string plaintext;
+    ASSERT_FALSE(encryptor.DecryptString("a", &plaintext));
+    ASSERT_TRUE(plaintext.empty());
+  }
 }
 #endif  // BUILDFLAG(IS_WIN)
 
@@ -700,6 +724,31 @@
   }
 }
 
+TEST_F(EncryptorTestWithOSCrypt, DecryptFlags) {
+  std::string ciphertext;
+  {
+    Encryptor::KeyRing key_ring;
+    key_ring.emplace("TEST", DeriveAES256TestKey("TEST"));
+    const auto encryptor = GetEncryptor(std::move(key_ring), "TEST");
+    ASSERT_TRUE(encryptor.EncryptString("secrets", &ciphertext));
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    EXPECT_FALSE(flags.should_reencrypt);
+  }
+
+  {
+    Encryptor::KeyRing key_ring;
+    key_ring.emplace("BLAH", DeriveAES256TestKey("BLAH"));
+    key_ring.emplace("TEST", DeriveAES256TestKey("TEST"));
+    const auto encryptor = GetEncryptor(std::move(key_ring), "BLAH");
+    Encryptor::DecryptFlags flags;
+    std::string plaintext;
+    ASSERT_TRUE(encryptor.DecryptString(ciphertext, &plaintext, &flags));
+    EXPECT_TRUE(flags.should_reencrypt);
+  }
+}
+
 class EncryptorTraitsTest : public EncryptorTestBase {};
 
 TEST_F(EncryptorTraitsTest, TraitsRoundTrip) {
diff --git a/components/os_crypt/sync/os_crypt_posix.cc b/components/os_crypt/sync/os_crypt_posix.cc
index ed33736..b6886d6 100644
--- a/components/os_crypt/sync/os_crypt_posix.cc
+++ b/components/os_crypt/sync/os_crypt_posix.cc
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "base/check.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "crypto/encryptor.h"
@@ -117,8 +118,13 @@
   // old data saved as clear text and we'll return it directly.
   // Credit card numbers are current legacy data, so false match with prefix
   // won't happen.
-  if (!base::StartsWith(ciphertext, kObfuscationPrefix,
-                        base::CompareCase::SENSITIVE)) {
+  const bool no_prefix_found = !base::StartsWith(ciphertext, kObfuscationPrefix,
+                                                 base::CompareCase::SENSITIVE);
+
+  base::UmaHistogramBoolean("OSCrypt.Posix.NoEncryptionPrefixFound",
+                            no_prefix_found);
+
+  if (no_prefix_found) {
     *plaintext = ciphertext;
     return true;
   }
diff --git a/components/os_crypt/sync/os_crypt_unittest.cc b/components/os_crypt/sync/os_crypt_unittest.cc
index 07eae05..492736c 100644
--- a/components/os_crypt/sync/os_crypt_unittest.cc
+++ b/components/os_crypt/sync/os_crypt_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/functional/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -355,4 +356,37 @@
 
 #endif  // BUILDFLAG(IS_WIN)
 
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) &&         \
+        !(BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)) || \
+    BUILDFLAG(IS_FUCHSIA)
+// os_crypt_posix.cc has no mocker, so it can be a standalone fixture.
+TEST(OSCrypt, PosixMetric) {
+  {
+    std::string ciphertext;
+    EXPECT_TRUE(OSCrypt::EncryptString("secret", &ciphertext));
+    base::HistogramTester histograms;
+    std::string plaintext;
+    EXPECT_TRUE(OSCrypt::DecryptString(ciphertext, &plaintext));
+    histograms.ExpectUniqueSample("OSCrypt.Posix.NoEncryptionPrefixFound",
+                                  false, 1u);
+  }
+
+  {
+    base::HistogramTester histograms;
+    std::string plaintext;
+    EXPECT_TRUE(OSCrypt::DecryptString("invaliddata!", &plaintext));
+    histograms.ExpectUniqueSample("OSCrypt.Posix.NoEncryptionPrefixFound", true,
+                                  1u);
+  }
+
+  {
+    base::HistogramTester histograms;
+    std::string plaintext;
+    // Empty string should not set this histogram.
+    EXPECT_TRUE(OSCrypt::DecryptString(std::string(), &plaintext));
+    histograms.ExpectTotalCount("OSCrypt.Posix.NoEncryptionPrefixFound", 0u);
+  }
+}
+#endif
+
 }  // namespace
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/CreateThemesSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/CreateThemesSettings.yaml
index f902544..c51a0e1 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/CreateThemesSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/CreateThemesSettings.yaml
@@ -11,7 +11,6 @@
 
   For more information on data handling for generative AI features, please see https://support.google.com/chrome/a?p=generative_ai_settings.
 default: 0
-default_for_enterprise_users: 1
 example_value: 2
 features:
   dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/DevToolsGenAiSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/DevToolsGenAiSettings.yaml
index f392c33..b94bbdc 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/DevToolsGenAiSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/DevToolsGenAiSettings.yaml
@@ -13,7 +13,6 @@
 
   - Console Insights: explains console messages and offers suggestions on how to fix console errors.
 default: 0
-default_for_enterprise_users: 1
 example_value: 2
 features:
   dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAIVcBackgroundSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAIVcBackgroundSettings.yaml
index 8e4b3622..0780e49 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAIVcBackgroundSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAIVcBackgroundSettings.yaml
@@ -10,7 +10,6 @@
 
   For more information on data handling for generative AI features, please see https://support.google.com/chrome/a?p=generative_ai_settings.
 default: 0
-default_for_enterprise_users: 1
 example_value: 2
 features:
   dynamic_refresh: true
@@ -36,4 +35,4 @@
 future_on:
 - chrome_os
 tags: []
-type: int-enum
\ No newline at end of file
+type: int-enum
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAIWallpaperSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAIWallpaperSettings.yaml
index 6d67fba5..7a78a44c 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAIWallpaperSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAIWallpaperSettings.yaml
@@ -10,7 +10,6 @@
 
   For more information on data handling for generative AI features, please see https://support.google.com/chrome/a?p=generative_ai_settings.
 default: 0
-default_for_enterprise_users: 1
 example_value: 2
 features:
   dynamic_refresh: true
@@ -36,4 +35,4 @@
 future_on:
 - chrome_os
 tags: []
-type: int-enum
\ No newline at end of file
+type: int-enum
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAiDefaultSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAiDefaultSettings.yaml
index 1862ee0..b2e72b63 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAiDefaultSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/GenAiDefaultSettings.yaml
@@ -21,11 +21,7 @@
   dynamic_refresh: true
   per_profile: true
 future_on:
-- android
-- chrome.*
-- chrome_os
 - fuchsia
-- ios
 items:
 - caption: Allow GenAI features and improve AI models
   name: Allowed
@@ -45,5 +41,10 @@
   - 1
   - 2
   type: integer
+supported_on:
+- android:130-
+- chrome.*:130-
+- chrome_os:130-
+- ios:130-
 tags: []
 type: int-enum
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/HelpMeWriteSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/HelpMeWriteSettings.yaml
index 38ef64f..b206dc8 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/HelpMeWriteSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/HelpMeWriteSettings.yaml
@@ -11,7 +11,6 @@
 
   For more information on data handling for generative AI features, please see https://support.google.com/chrome/a?p=generative_ai_settings.
 default: 0
-default_for_enterprise_users: 1
 example_value: 2
 features:
   dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/HistorySearchSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/HistorySearchSettings.yaml
index f80ccf9..fe53912 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/HistorySearchSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/HistorySearchSettings.yaml
@@ -13,7 +13,6 @@
 
   For more information on data handling for generative AI features, please see https://support.google.com/chrome/a?p=generative_ai_settings.
 default: 0
-default_for_enterprise_users: 1
 example_value: 2
 features:
   dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/TabCompareSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/TabCompareSettings.yaml
index c7aff44..67b665f 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/TabCompareSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/TabCompareSettings.yaml
@@ -1,6 +1,5 @@
 caption: Tab Compare settings
 default: 0
-default_for_enterprise_users: 1
 desc: |-
   Tab Compare is an AI-powered tool for comparing information across a user's tabs. As an example, the feature can be offered to the user when multiple tabs with products in a similar category are open.
 
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/TabOrganizerSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/TabOrganizerSettings.yaml
index a01df73..a576cc7 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/TabOrganizerSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/TabOrganizerSettings.yaml
@@ -11,7 +11,6 @@
 
   For more information on data handling for generative AI features, please see https://support.google.com/chrome/a?p=generative_ai_settings.
 default: 0
-default_for_enterprise_users: 1
 example_value: 2
 features:
   dynamic_refresh: true
diff --git a/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json b/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
index 03be3552..0567d038 100644
--- a/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
+++ b/components/policy/test/data/pref_mapping/GenAiDefaultSettings.json
@@ -138,7 +138,6 @@
     ]
   },
   {
-    "note": "The default does not apply on Chrome OS yet because of default_for_enterprise_users. This will be updated in an upcoming change.",
     "os": [
       "chromeos_lacros",
       "chromeos_ash"
@@ -150,28 +149,28 @@
         },
         "prefs": {
           "optimization_guide.model_execution.compose_enterprise_policy_allowed": {
-            "value": 1
+            "value": 0
           },
           "optimization_guide.model_execution.tab_organization_enterprise_policy_allowed": {
-            "value": 1
+            "value": 0
           },
           "optimization_guide.model_execution.wallpaper_search_enterprise_policy_allowed": {
-            "value": 1
+            "value": 0
           },
           "devtools.gen_ai_settings": {
-            "value": 1
+            "value": 0
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
-            "value": 1
+            "value": 0
           },
           "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
-            "value": 1
+            "value": 0
           },
           "ash.personalization_app.gen_ai_wallpaper_enterprise_policy_settings": {
-            "value": 1
+            "value": 0
           },
           "ash.vc_background_ui.gen_ai_vc_background_enterprise_policy_settings": {
-            "value": 1
+            "value": 0
           }
         }
       },
@@ -207,27 +206,33 @@
       {
         "policies": {
           "GenAiDefaultSettings": 0,
+          "HelpMeWriteSettings": 2,
+          "TabOrganizerSettings": 2,
+          "CreateThemesSettings": 2,
+          "DevToolsGenAiSettings": 2,
+          "HistorySearchSettings": 2,
+          "TabCompareSettings": 2,
           "GenAIWallpaperSettings": 2,
           "GenAIVcBackgroundSettings": 2
         },
         "prefs": {
           "optimization_guide.model_execution.compose_enterprise_policy_allowed": {
-            "value": 1
+            "value": 2
           },
           "optimization_guide.model_execution.tab_organization_enterprise_policy_allowed": {
-            "value": 1
+            "value": 2
           },
           "optimization_guide.model_execution.wallpaper_search_enterprise_policy_allowed": {
-            "value": 1
+            "value": 2
           },
           "devtools.gen_ai_settings": {
-            "value": 1
+            "value": 2
           },
           "optimization_guide.model_execution.history_search_enterprise_policy_allowed": {
-            "value": 1
+            "value": 2
           },
           "optimization_guide.model_execution.tab_compare_settings_enterprise_policy": {
-            "value": 1
+            "value": 2
           },
           "ash.personalization_app.gen_ai_wallpaper_enterprise_policy_settings": {
             "value": 2
diff --git a/components/remote_cocoa/app_shim/menu_controller_cocoa_delegate_impl.mm b/components/remote_cocoa/app_shim/menu_controller_cocoa_delegate_impl.mm
index 2844661b..311db974 100644
--- a/components/remote_cocoa/app_shim/menu_controller_cocoa_delegate_impl.mm
+++ b/components/remote_cocoa/app_shim/menu_controller_cocoa_delegate_impl.mm
@@ -305,13 +305,15 @@
       }
 
       // We expect to see the following order of events:
+      //
       // - element shown
       // - element activated (optional)
       // - element hidden
-      // However, the code that detects menu item activation is called *after*
-      // the current callback. To make sure the events happen in the right order
-      // we'll defer processing of element hidden events until the end of the
-      // current system event queue.
+      //
+      // However, the OS notification for "element activated" fires *after* the
+      // NSMenuDidEndTrackingNotification notification that is used here for
+      // "element hidden". Therefore, to ensure correct ordering, defer
+      // processing "element hidden" by posting to the main dispatch queue.
       dispatch_after(
           dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC),
           dispatch_get_main_queue(), ^{
diff --git a/components/safe_browsing/core/browser/db/util.cc b/components/safe_browsing/core/browser/db/util.cc
index fb3c3ae..6d05eb9 100644
--- a/components/safe_browsing/core/browser/db/util.cc
+++ b/components/safe_browsing/core/browser/db/util.cc
@@ -13,7 +13,14 @@
 
 ThreatMetadata::ThreatMetadata(const ThreatMetadata& other) = default;
 
-ThreatMetadata::~ThreatMetadata() {}
+ThreatMetadata::ThreatMetadata(ThreatMetadata&& other) = default;
+
+ThreatMetadata& ThreatMetadata::operator=(const ThreatMetadata& other) =
+    default;
+
+ThreatMetadata& ThreatMetadata::operator=(ThreatMetadata&& other) = default;
+
+ThreatMetadata::~ThreatMetadata() = default;
 
 bool ThreatMetadata::operator==(const ThreatMetadata& other) const {
   return threat_pattern_type == other.threat_pattern_type &&
diff --git a/components/safe_browsing/core/browser/db/util.h b/components/safe_browsing/core/browser/db/util.h
index f6ad3990..8a8fcaa 100644
--- a/components/safe_browsing/core/browser/db/util.h
+++ b/components/safe_browsing/core/browser/db/util.h
@@ -49,6 +49,9 @@
 struct ThreatMetadata {
   ThreatMetadata();
   ThreatMetadata(const ThreatMetadata& other);
+  ThreatMetadata(ThreatMetadata&& other);
+  ThreatMetadata& operator=(const ThreatMetadata& other);
+  ThreatMetadata& operator=(ThreatMetadata&& other);
   ~ThreatMetadata();
 
   bool operator==(const ThreatMetadata& other) const;
diff --git a/components/security_interstitials/content/renderer/BUILD.gn b/components/security_interstitials/content/renderer/BUILD.gn
index 32bb1deb..672e2c4 100644
--- a/components/security_interstitials/content/renderer/BUILD.gn
+++ b/components/security_interstitials/content/renderer/BUILD.gn
@@ -1,11 +1,13 @@
-static_library("security_interstitial_page_controller") {
+static_library("renderer") {
   sources = [
     "security_interstitial_page_controller.cc",
     "security_interstitial_page_controller.h",
+    "security_interstitial_page_controller_delegate_impl.cc",
+    "security_interstitial_page_controller_delegate_impl.h",
   ]
   deps = [
     "//base",
-    "//components/security_interstitials/core:",
+    "//components/security_interstitials/core",
     "//content/public/renderer",
     "//gin",
     "//third_party/blink/public:blink",
diff --git a/components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.cc b/components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.cc
new file mode 100644
index 0000000..26859649
--- /dev/null
+++ b/components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.cc
@@ -0,0 +1,42 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.h"
+
+#include "content/public/renderer/render_frame.h"
+
+namespace security_interstitials {
+
+SecurityInterstitialPageControllerDelegateImpl::
+    SecurityInterstitialPageControllerDelegateImpl(
+        content::RenderFrame* render_frame)
+    : content::RenderFrameObserver(render_frame),
+      content::RenderFrameObserverTracker<
+          SecurityInterstitialPageControllerDelegateImpl>(render_frame) {}
+
+SecurityInterstitialPageControllerDelegateImpl::
+    ~SecurityInterstitialPageControllerDelegateImpl() = default;
+
+void SecurityInterstitialPageControllerDelegateImpl::PrepareForErrorPage() {
+  pending_error_ = true;
+}
+
+void SecurityInterstitialPageControllerDelegateImpl::OnDestruct() {
+  delete this;
+}
+
+void SecurityInterstitialPageControllerDelegateImpl::DidCommitProvisionalLoad(
+    ui::PageTransition transition) {
+  committed_error_ = pending_error_;
+  pending_error_ = false;
+}
+
+void SecurityInterstitialPageControllerDelegateImpl::DidFinishLoad() {
+  if (committed_error_) {
+    security_interstitials::SecurityInterstitialPageController::Install(
+        render_frame());
+  }
+}
+
+}  // namespace security_interstitials
diff --git a/components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.h b/components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.h
new file mode 100644
index 0000000..b06ef1c7
--- /dev/null
+++ b/components/security_interstitials/content/renderer/security_interstitial_page_controller_delegate_impl.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 COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_RENDERER_SECURITY_INTERSTITIAL_PAGE_CONTROLLER_DELEGATE_IMPL_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_RENDERER_SECURITY_INTERSTITIAL_PAGE_CONTROLLER_DELEGATE_IMPL_H_
+
+#include "components/security_interstitials/content/renderer/security_interstitial_page_controller.h"
+#include "components/security_interstitials/core/controller_client.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "content/public/renderer/render_frame_observer_tracker.h"
+
+namespace content {
+class RenderFrame;
+}  // namespace content
+
+namespace security_interstitials {
+
+class SecurityInterstitialPageControllerDelegateImpl
+    : public content::RenderFrameObserver,
+      public content::RenderFrameObserverTracker<
+          SecurityInterstitialPageControllerDelegateImpl> {
+ public:
+  explicit SecurityInterstitialPageControllerDelegateImpl(
+      content::RenderFrame* render_frame);
+
+  // Disallow copy and assign
+  SecurityInterstitialPageControllerDelegateImpl(
+      const SecurityInterstitialPageControllerDelegateImpl&) = delete;
+  SecurityInterstitialPageControllerDelegateImpl& operator=(
+      const SecurityInterstitialPageControllerDelegateImpl&) = delete;
+
+  ~SecurityInterstitialPageControllerDelegateImpl() override;
+
+  // Notifies us that a navigation error has occurred and will be committed
+  void PrepareForErrorPage();
+
+  // content::RenderFrameObserver:
+  void OnDestruct() override;
+  void DidCommitProvisionalLoad(ui::PageTransition transition) override;
+  void DidFinishLoad() override;
+
+ private:
+  // Whether there is an error page pending to be committed.
+  bool pending_error_ = false;
+
+  // Whether the committed page is an error page.
+  bool committed_error_ = false;
+};
+
+}  // namespace security_interstitials
+
+#endif  // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_RENDERER_SECURITY_INTERSTITIAL_PAGE_CONTROLLER_DELEGATE_IMPL_H_
diff --git a/components/security_interstitials/core/browser/resources/interstitial_large.html b/components/security_interstitials/core/browser/resources/interstitial_large.html
index 39ed675..0b77160 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_large.html
+++ b/components/security_interstitials/core/browser/resources/interstitial_large.html
@@ -39,6 +39,10 @@
       <div id="recurrent-error-message">
         $i18nRaw{recurrentErrorParagraph}
       </div>
+      <div id="blocked-site-message" class="hidden">
+        <p id="blocked-site-message-header"></p>
+        <p id="blocked-site-message-reason"></p>
+      </div>
       <div id="extended-reporting-opt-in" class="hidden">
         <label>
           <div class="checkboxes">
diff --git a/components/security_interstitials/core/browser/resources/interstitial_large.js b/components/security_interstitials/core/browser/resources/interstitial_large.js
index 57ace31..3967890 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_large.js
+++ b/components/security_interstitials/core/browser/resources/interstitial_large.js
@@ -81,6 +81,10 @@
   const hidePrimaryButton = loadTimeData.getBoolean('hide_primary_button');
   const showRecurrentErrorParagraph =
       loadTimeData.getBoolean('show_recurrent_error_paragraph');
+  const showBlockedSiteMessage =
+      loadTimeData.valueExists('show_blocked_site_message') ?
+      loadTimeData.getBoolean('show_blocked_site_message') :
+      false;
 
   const body = document.querySelector('#body');
   if (ssl || blockedInterception) {
@@ -208,6 +212,16 @@
     body.classList.add('showing-recurrent-error-message');
   }
 
+  if (showBlockedSiteMessage) {
+    document.querySelector('#blocked-site-message')
+        .classList.remove(HIDDEN_CLASS);
+    body.classList.add('showing-blocked-site-message');
+    document.getElementById('blocked-site-message-header').textContent =
+        loadTimeData.getString('blockedSiteMessageHeader');
+    document.getElementById('blocked-site-message-reason').textContent =
+        loadTimeData.getString('blockedSiteMessageReason');
+  }
+
   const diagnosticLink = document.querySelector('#diagnostic-link');
   if (diagnosticLink) {
     diagnosticLink.addEventListener('click', function(event) {
diff --git a/components/security_interstitials/core/browser/resources/interstitial_superviseduserverify.css b/components/security_interstitials/core/browser/resources/interstitial_superviseduserverify.css
index 1e0724eb..4290ba2a 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_superviseduserverify.css
+++ b/components/security_interstitials/core/browser/resources/interstitial_superviseduserverify.css
@@ -3,6 +3,13 @@
  * found in the LICENSE file.
  */
 
+body.showing-blocked-site-message {
+  --header-color: var(--google-gray-900);
+  --paragraph-color: var(--google-gray-700);
+  --callout-text-color: var(--google-gray-900);
+  --callout-bg-color: var(--google-gray-100);
+}
+
 .supervised-user-verify #main-content a {
   color: var(--google-blue-700);
   text-decoration: none;
@@ -14,8 +21,38 @@
   height: 120px;
 }
 
+.showing-blocked-site-message {
+  #blocked-site-message {
+    align-items: center;
+    background-color: var(--callout-bg-color);
+    border-radius: 8px;
+    color: var(--header-color);
+    justify-content: flex-start;
+    padding: 16px;
+    width: max-content;
+  }
+
+  #blocked-site-message-header,
+  #blocked-site-message-reason {
+    background-color: var(--callout-bg-color);
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  #blocked-site-message-reason {
+    font-size: 0.8em;
+  }
+}
+
 @media (prefers-color-scheme: dark) {
   .supervised-user-verify .icon {
     background-image: url(images/error_page_illustration_dark_theme.svg);
   }
+
+  body.showing-blocked-site-message {
+    --callout-bg-color: var(--google-gray-800);
+    --callout-text-color: var(--google-gray-200);
+    --header-color: #e9eaed;
+    --paragraph-color: var(--google-gray-500);
+  }
 }
diff --git a/components/sessions/core/live_tab_context.h b/components/sessions/core/live_tab_context.h
index d21452f..a720e80 100644
--- a/components/sessions/core/live_tab_context.h
+++ b/components/sessions/core/live_tab_context.h
@@ -17,6 +17,7 @@
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/base/ui_base_types.h"
 
 namespace base {
@@ -64,7 +65,7 @@
       const tab_groups::TabGroupId& group,
       const tab_groups::TabGroupVisualData& visual_data) = 0;
   virtual const gfx::Rect GetRestoredBounds() const = 0;
-  virtual ui::WindowShowState GetRestoredState() const = 0;
+  virtual ui::mojom::WindowShowState GetRestoredState() const = 0;
   virtual std::string GetWorkspace() const = 0;
 
   // Note: |tab.platform_data| may be null (e.g., if restoring from last session
diff --git a/components/sessions/core/session_service_commands.cc b/components/sessions/core/session_service_commands.cc
index fc9d96c..6b72cf1 100644
--- a/components/sessions/core/session_service_commands.cc
+++ b/components/sessions/core/session_service_commands.cc
@@ -21,6 +21,7 @@
 #include "base/values.h"
 #include "components/sessions/core/base_session_service_commands.h"
 #include "components/tab_groups/tab_group_color.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 
 namespace sessions {
 
@@ -156,8 +157,8 @@
   bool visible_on_all_workspaces;
 };
 
-// Persisted versions of ui::WindowShowState that are written to disk and can
-// never change.
+// Persisted versions of ui::mojom::WindowShowState that are written to disk and
+// can never change.
 enum PersistedWindowShowState {
   // SHOW_STATE_DEFAULT (0) never persisted.
   PERSISTED_SHOW_STATE_NORMAL = 1,
@@ -179,28 +180,29 @@
 
 // Assert to ensure PersistedWindowShowState is updated if ui::WindowShowState
 // is changed.
-static_assert(ui::SHOW_STATE_END ==
-                  static_cast<ui::WindowShowState>(PERSISTED_SHOW_STATE_END -
-                                                   2),
-              "SHOW_STATE_END must equal PERSISTED_SHOW_STATE_END minus the "
-              "deprecated entries");
+// TODO(crbug.com/361560784): Investigate and Remove `kEnd`
+static_assert(
+    ui::mojom::WindowShowState::kEnd ==
+        static_cast<ui::mojom::WindowShowState>(PERSISTED_SHOW_STATE_END - 2),
+    "WindowShowState::kEnd must equal PERSISTED_SHOW_STATE_END minus the "
+    "deprecated entries");
 // Returns the show state to store to disk based |state|.
 PersistedWindowShowState ShowStateToPersistedShowState(
-    ui::WindowShowState state) {
+    ui::mojom::WindowShowState state) {
   switch (state) {
-    case ui::SHOW_STATE_NORMAL:
+    case ui::mojom::WindowShowState::kNormal:
       return PERSISTED_SHOW_STATE_NORMAL;
-    case ui::SHOW_STATE_MINIMIZED:
+    case ui::mojom::WindowShowState::kMinimized:
       return PERSISTED_SHOW_STATE_MINIMIZED;
-    case ui::SHOW_STATE_MAXIMIZED:
+    case ui::mojom::WindowShowState::kMaximized:
       return PERSISTED_SHOW_STATE_MAXIMIZED;
-    case ui::SHOW_STATE_FULLSCREEN:
+    case ui::mojom::WindowShowState::kFullscreen:
       return PERSISTED_SHOW_STATE_FULLSCREEN;
-    case ui::SHOW_STATE_DEFAULT:
-    case ui::SHOW_STATE_INACTIVE:
+    case ui::mojom::WindowShowState::kDefault:
+    case ui::mojom::WindowShowState::kInactive:
       return PERSISTED_SHOW_STATE_NORMAL;
 
-    case ui::SHOW_STATE_END:
+    case ui::mojom::WindowShowState::kEnd:
       break;
   }
   NOTREACHED_IN_MIGRATION();
@@ -208,22 +210,22 @@
 }
 
 // Lints show state values when read back from persited disk.
-ui::WindowShowState PersistedShowStateToShowState(int state) {
+ui::mojom::WindowShowState PersistedShowStateToShowState(int state) {
   switch (state) {
     case PERSISTED_SHOW_STATE_NORMAL:
-      return ui::SHOW_STATE_NORMAL;
+      return ui::mojom::WindowShowState::kNormal;
     case PERSISTED_SHOW_STATE_MINIMIZED:
-      return ui::SHOW_STATE_MINIMIZED;
+      return ui::mojom::WindowShowState::kMinimized;
     case PERSISTED_SHOW_STATE_MAXIMIZED:
-      return ui::SHOW_STATE_MAXIMIZED;
+      return ui::mojom::WindowShowState::kMaximized;
     case PERSISTED_SHOW_STATE_FULLSCREEN:
-      return ui::SHOW_STATE_FULLSCREEN;
+      return ui::mojom::WindowShowState::kFullscreen;
     case PERSISTED_SHOW_STATE_DETACHED_DEPRECATED:
     case PERSISTED_SHOW_STATE_DOCKED_DEPRECATED:
-      return ui::SHOW_STATE_NORMAL;
+      return ui::mojom::WindowShowState::kNormal;
   }
   DUMP_WILL_BE_NOTREACHED();
-  return ui::SHOW_STATE_NORMAL;
+  return ui::mojom::WindowShowState::kNormal;
 }
 
 // Iterates through the vector updating the selected_tab_index of each
@@ -488,8 +490,8 @@
         GetWindow(window_id, windows)
             ->bounds.SetRect(payload.x, payload.y, payload.w, payload.h);
         GetWindow(window_id, windows)->show_state =
-            payload.is_maximized ? ui::SHOW_STATE_MAXIMIZED
-                                 : ui::SHOW_STATE_NORMAL;
+            payload.is_maximized ? ui::mojom::WindowShowState::kMaximized
+                                 : ui::mojom::WindowShowState::kNormal;
         break;
       }
 
@@ -967,7 +969,7 @@
 std::unique_ptr<SessionCommand> CreateSetWindowBoundsCommand(
     SessionID window_id,
     const gfx::Rect& bounds,
-    ui::WindowShowState show_state) {
+    ui::mojom::WindowShowState show_state) {
   WindowBoundsPayload3 payload = { 0 };
   payload.window_id = window_id.id();
   payload.x = bounds.x();
diff --git a/components/sessions/core/session_service_commands.h b/components/sessions/core/session_service_commands.h
index b17ec41b..73bc413 100644
--- a/components/sessions/core/session_service_commands.h
+++ b/components/sessions/core/session_service_commands.h
@@ -16,6 +16,7 @@
 #include "components/sessions/core/sessions_export.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/base/ui_base_types.h"
 
 namespace sessions {
@@ -32,7 +33,7 @@
 SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateSetWindowBoundsCommand(
     SessionID window_id,
     const gfx::Rect& bounds,
-    ui::WindowShowState show_state);
+    ui::mojom::WindowShowState show_state);
 SESSIONS_EXPORT std::unique_ptr<SessionCommand>
 CreateSetTabIndexInWindowCommand(SessionID tab_id, int new_index);
 SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateTabClosedCommand(
diff --git a/components/sessions/core/session_types.cc b/components/sessions/core/session_types.cc
index 15e90e4b..1c271af2 100644
--- a/components/sessions/core/session_types.cc
+++ b/components/sessions/core/session_types.cc
@@ -8,6 +8,7 @@
 
 #include "components/sessions/core/session_command.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 
 namespace sessions {
 
@@ -37,7 +38,7 @@
       selected_tab_index(-1),
       type(TYPE_NORMAL),
       is_constrained(true),
-      show_state(ui::SHOW_STATE_DEFAULT) {}
+      show_state(ui::mojom::WindowShowState::kDefault) {}
 
 SessionWindow::~SessionWindow() {}
 
diff --git a/components/sessions/core/session_types.h b/components/sessions/core/session_types.h
index 5366a28..aa5bcf1 100644
--- a/components/sessions/core/session_types.h
+++ b/components/sessions/core/session_types.h
@@ -23,6 +23,7 @@
 #include "components/tab_groups/tab_group_visual_data.h"
 #include "components/variations/variations_associated_data.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/rect.h"
@@ -202,7 +203,7 @@
   std::vector<std::unique_ptr<SessionTabGroup>> tab_groups;
 
   // Is the window maximized, minimized, or normal?
-  ui::WindowShowState show_state;
+  ui::mojom::WindowShowState show_state;
 
   std::string app_name;
 
diff --git a/components/sessions/core/tab_restore_service_client.h b/components/sessions/core/tab_restore_service_client.h
index 13d01e0..2fcfd86 100644
--- a/components/sessions/core/tab_restore_service_client.h
+++ b/components/sessions/core/tab_restore_service_client.h
@@ -13,6 +13,7 @@
 #include "components/sessions/core/session_id.h"
 #include "components/sessions/core/session_types.h"
 #include "components/sessions/core/sessions_export.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/base/ui_base_types.h"
 
 class GURL;
@@ -50,7 +51,7 @@
       SessionWindow::WindowType type,
       const std::string& app_name,
       const gfx::Rect& bounds,
-      ui::WindowShowState show_state,
+      ui::mojom::WindowShowState show_state,
       const std::string& workspace,
       const std::string& user_title,
       const std::map<std::string, std::string>& extra_data) = 0;
diff --git a/components/sessions/core/tab_restore_service_helper.cc b/components/sessions/core/tab_restore_service_helper.cc
index d38af4b..778de61 100644
--- a/components/sessions/core/tab_restore_service_helper.cc
+++ b/components/sessions/core/tab_restore_service_helper.cc
@@ -42,6 +42,7 @@
 #include "components/sessions/core/tab_restore_types.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 #include "ui/base/window_open_disposition.h"
 
 namespace sessions {
@@ -980,7 +981,7 @@
     } else {
       context = client_->CreateLiveTabContext(
           context, SessionWindow::TYPE_NORMAL, std::string(), gfx::Rect(),
-          ui::SHOW_STATE_NORMAL, std::string(), std::string(),
+          ui::mojom::WindowShowState::kNormal, std::string(), std::string(),
           std::map<std::string, std::string>());
       if (tab.browser_id) {
         UpdateTabBrowserIDs(tab.browser_id, context->GetSessionID());
diff --git a/components/sessions/core/tab_restore_service_impl.cc b/components/sessions/core/tab_restore_service_impl.cc
index 791fc18..514f81ee 100644
--- a/components/sessions/core/tab_restore_service_impl.cc
+++ b/components/sessions/core/tab_restore_service_impl.cc
@@ -38,6 +38,7 @@
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "ui/base/mojom/window_show_state.mojom.h"
 
 #undef LoadBitmap
 
@@ -190,21 +191,21 @@
 
 // Converts a window show state to an integer. This function needs to be kept
 // up to date with the SerializedWindowShowState enum.
-int SerializeWindowShowState(ui::WindowShowState show_state) {
+int SerializeWindowShowState(ui::mojom::WindowShowState show_state) {
   switch (show_state) {
-    case ui::SHOW_STATE_DEFAULT:
+    case ui::mojom::WindowShowState::kDefault:
       return kSerializedShowStateDefault;
-    case ui::SHOW_STATE_NORMAL:
+    case ui::mojom::WindowShowState::kNormal:
       return kSerializedShowStateNormal;
-    case ui::SHOW_STATE_MINIMIZED:
+    case ui::mojom::WindowShowState::kMinimized:
       return kSerializedShowStateMinimized;
-    case ui::SHOW_STATE_MAXIMIZED:
+    case ui::mojom::WindowShowState::kMaximized:
       return kSerializedShowStateMaximized;
-    case ui::SHOW_STATE_INACTIVE:
+    case ui::mojom::WindowShowState::kInactive:
       return kSerializedShowStateInactive;
-    case ui::SHOW_STATE_FULLSCREEN:
+    case ui::mojom::WindowShowState::kFullscreen:
       return kSerializedShowStateFullscreen;
-    case ui::SHOW_STATE_END:
+    case ui::mojom::WindowShowState::kEnd:
       // This should never happen.
       NOTREACHED_IN_MIGRATION();
   }
@@ -215,25 +216,25 @@
 // otherwise. This function needs to be kept up to date with the
 // SerializedWindowShowState enum.
 bool DeserializeWindowShowState(int show_state_int,
-                                ui::WindowShowState* show_state) {
+                                ui::mojom::WindowShowState* show_state) {
   switch (static_cast<SerializedWindowShowState>(show_state_int)) {
     case kSerializedShowStateDefault:
-      *show_state = ui::SHOW_STATE_DEFAULT;
+      *show_state = ui::mojom::WindowShowState::kDefault;
       return true;
     case kSerializedShowStateNormal:
-      *show_state = ui::SHOW_STATE_NORMAL;
+      *show_state = ui::mojom::WindowShowState::kNormal;
       return true;
     case kSerializedShowStateMinimized:
-      *show_state = ui::SHOW_STATE_MINIMIZED;
+      *show_state = ui::mojom::WindowShowState::kMinimized;
       return true;
     case kSerializedShowStateMaximized:
-      *show_state = ui::SHOW_STATE_MAXIMIZED;
+      *show_state = ui::mojom::WindowShowState::kMaximized;
       return true;
     case kSerializedShowStateInactive:
-      *show_state = ui::SHOW_STATE_INACTIVE;
+      *show_state = ui::mojom::WindowShowState::kInactive;
       return true;
     case kSerializedShowStateFullscreen:
-      *show_state = ui::SHOW_STATE_FULLSCREEN;
+      *show_state = ui::mojom::WindowShowState::kFullscreen;
       return true;
     case kSerializedShowStateInvalid:
     default:
@@ -291,7 +292,7 @@
     SessionID* window_id,
     int32_t* num_tabs) {
   WindowCommandFields fields;
-  ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
+  ui::mojom::WindowShowState show_state = ui::mojom::WindowShowState::kDefault;
   auto type = sessions::SessionWindow::TYPE_NORMAL;
 
   if (command->id() == kCommandWindow) {
@@ -541,7 +542,7 @@
       int selected_tab_index,
       int num_tabs,
       const gfx::Rect& bounds,
-      ui::WindowShowState show_state,
+      ui::mojom::WindowShowState show_state,
       const std::string& workspace,
       base::Time timestamp);
 
@@ -941,7 +942,7 @@
     int selected_tab_index,
     int num_tabs,
     const gfx::Rect& bounds,
-    ui::WindowShowState show_state,
+    ui::mojom::WindowShowState show_state,
     const std::string& workspace,
     base::Time timestamp) {
   static_assert(sizeof(SessionID::id_type) == sizeof(int),
diff --git a/components/sessions/core/tab_restore_types.h b/components/sessions/core/tab_restore_types.h
index 82ebaf87..230679d 100644
--- a/components/sessions/core/tab_restore_types.h
+++ b/components/sessions/core/tab_restore_types.h
@@ -19,6 +19,7 @@
 #include "components/sessions/core/sessions_export.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace sessions::tab_restore {
@@ -193,7 +194,7 @@
 
   // Where and how the window is displayed.
   gfx::Rect bounds;
-  ui::WindowShowState show_state;
+  ui::mojom::WindowShowState show_state;
   std::string workspace;
 };
 
diff --git a/components/signin/public/identity_manager/access_token_restriction.cc b/components/signin/public/identity_manager/access_token_restriction.cc
index 5dfd99fe..3754fce 100644
--- a/components/signin/public/identity_manager/access_token_restriction.cc
+++ b/components/signin/public/identity_manager/access_token_restriction.cc
@@ -131,7 +131,7 @@
       GaiaConstants::kDriveReadOnlyOAuth2Scope,
       GaiaConstants::kExperimentsAndConfigsOAuth2Scope,
       GaiaConstants::kGCMGroupServerOAuth2Scope,
-      GaiaConstants::kCloudPlatformProjectsOAuth2Scope,
+      GaiaConstants::kNearbyDevicesOAuth2Scope,
       GaiaConstants::kNearbyShareOAuth2Scope,
       GaiaConstants::kNearbyPresenceOAuth2Scope,
       GaiaConstants::kPeopleApiReadOnlyOAuth2Scope,
diff --git a/components/signin/public/identity_manager/access_token_restriction_unittest.cc b/components/signin/public/identity_manager/access_token_restriction_unittest.cc
index f82c9a05..4f8abfb 100644
--- a/components/signin/public/identity_manager/access_token_restriction_unittest.cc
+++ b/components/signin/public/identity_manager/access_token_restriction_unittest.cc
@@ -55,7 +55,7 @@
  {GaiaConstants::kDriveReadOnlyOAuth2Scope, OAuth2ScopeRestriction::kSignedIn},
  {GaiaConstants::kExperimentsAndConfigsOAuth2Scope, OAuth2ScopeRestriction::kSignedIn},
  {GaiaConstants::kGCMGroupServerOAuth2Scope, OAuth2ScopeRestriction::kSignedIn},
- {GaiaConstants::kCloudPlatformProjectsOAuth2Scope, OAuth2ScopeRestriction::kSignedIn},
+ {GaiaConstants::kNearbyDevicesOAuth2Scope, OAuth2ScopeRestriction::kSignedIn},
  {GaiaConstants::kNearbyShareOAuth2Scope, OAuth2ScopeRestriction::kSignedIn},
  {GaiaConstants::kNearbyPresenceOAuth2Scope, OAuth2ScopeRestriction::kSignedIn},
  {GaiaConstants::kPeopleApiReadOnlyOAuth2Scope, OAuth2ScopeRestriction::kSignedIn},
diff --git a/components/soda/BUILD.gn b/components/soda/BUILD.gn
index 1c7a9b3..d6b0266 100644
--- a/components/soda/BUILD.gn
+++ b/components/soda/BUILD.gn
@@ -63,6 +63,8 @@
     "//base",
     "//components/component_updater:component_updater_paths",
     "//components/crx_file",
+    "//components/language/core/browser",
+    "//components/prefs:prefs",
     "//components/strings/",
     "//media",
     "//ui/base",
diff --git a/components/soda/DEPS b/components/soda/DEPS
index 5b2c24a0..9746ae4 100644
--- a/components/soda/DEPS
+++ b/components/soda/DEPS
@@ -3,6 +3,7 @@
   "+chromeos/ash/components/dbus",
   "+components/component_updater/component_updater_paths.h",
   "+components/crx_file",
+  "+components/language/core/browser",
   "+components/live_caption",
   "+components/prefs",
   "+components/strings/grit/components_strings.h",
diff --git a/components/soda/constants.cc b/components/soda/constants.cc
index 5972aa78..e7ed3423 100644
--- a/components/soda/constants.cc
+++ b/components/soda/constants.cc
@@ -13,10 +13,13 @@
 #include "base/notreached.h"
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "components/component_updater/component_updater_paths.h"
 #include "components/crx_file/id_util.h"
+#include "components/language/core/browser/pref_names.h"
+#include "components/prefs/pref_service.h"
 #include "media/base/media_switches.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -253,4 +256,31 @@
       {"SodaInstaller.Language.", language, ".InstallationResult"});
 }
 
+const std::string GetDefaultLiveCaptionLanguage(
+    const std::string& application_locale,
+    PrefService* profile_prefs) {
+  std::optional<SodaLanguagePackComponentConfig> application_locale_config =
+      GetLanguageComponentConfigMatchingLanguageSubtag(application_locale);
+
+  if (application_locale_config.has_value() &&
+      application_locale_config.value().language_code != LanguageCode::kNone) {
+    return application_locale_config.value().language_name;
+  }
+
+  std::string accept_languages_pref =
+      profile_prefs->GetString(language::prefs::kAcceptLanguages);
+  for (std::string language :
+       base::SplitString(accept_languages_pref, ",", base::TRIM_WHITESPACE,
+                         base::SPLIT_WANT_NONEMPTY)) {
+    std::optional<SodaLanguagePackComponentConfig> config =
+        GetLanguageComponentConfigMatchingLanguageSubtag(language);
+    if (config.has_value() &&
+        config.value().language_code != LanguageCode::kNone) {
+      return config.value().language_name;
+    }
+  }
+
+  return kUsEnglishLocale;
+}
+
 }  // namespace speech
diff --git a/components/soda/constants.h b/components/soda/constants.h
index 1bd9f73..3e0f5f35 100644
--- a/components/soda/constants.h
+++ b/components/soda/constants.h
@@ -13,6 +13,8 @@
 #include "components/soda/pref_names.h"
 #include "components/strings/grit/components_strings.h"
 
+class PrefService;
+
 namespace speech {
 
 extern const char kUsEnglishLocale[];
@@ -313,6 +315,13 @@
 const std::string GetInstallationResultMetricForLanguage(
     const std::string& language);
 
+// Returns the available Live Caption language best matching the
+// application locale, one of the user's preferred languages, or en-US if none
+// of the other languages match.
+const std::string GetDefaultLiveCaptionLanguage(
+    const std::string& application_locale,
+    PrefService* profile_prefs);
+
 }  // namespace speech
 
 #endif  // COMPONENTS_SODA_CONSTANTS_H_
diff --git a/components/soda/soda_installer.h b/components/soda/soda_installer.h
index cd9e27f..2008038 100644
--- a/components/soda/soda_installer.h
+++ b/components/soda/soda_installer.h
@@ -153,6 +153,13 @@
   // Gets a list of locales enabled by the Finch flag.
   virtual std::vector<std::string> GetLiveCaptionEnabledLanguages() const;
 
+  // Registers a language pack by adding it to the preference tracking the
+  // installed SODA language packs.
+  void RegisterLanguage(const std::string& language, PrefService* global_prefs);
+
+  void UnregisterLanguage(const std::string& language,
+                          PrefService* global_prefs);
+
  protected:
   // Initializes language and installs the per-language components.
   virtual void InitLanguages(PrefService* profile_prefs,
@@ -180,17 +187,10 @@
   // 100.
   void NotifyOnSodaProgress(LanguageCode language_code, int progress);
 
-  // Registers a language pack by adding it to the preference tracking the
-  // installed SODA language packs.
-  void RegisterLanguage(const std::string& language, PrefService* global_prefs);
-
   // Unregisters all language packs by clearing the preference tracking the
   // installed SODA language packs.
   void UnregisterLanguages(PrefService* global_prefs);
 
-  void UnregisterLanguage(const std::string& language,
-                          PrefService* global_prefs);
-
   // Returns whether or not the language pack for a given language is
   // installed. The language should be localized in BCP-47, e.g. "en-US".
   bool IsLanguageInstalled(LanguageCode language_code) const;
diff --git a/components/sync/PRESUBMIT.py b/components/sync/PRESUBMIT.py
deleted file mode 100644
index adc0992a..0000000
--- a/components/sync/PRESUBMIT.py
+++ /dev/null
@@ -1,409 +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.
-
-"""Presubmit script for sync component.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-import os
-import re
-
-# Some definitions don't follow all the conventions we want to enforce.
-# It's either difficult or impossible to fix this, so we ignore the problem(s).
-EXCEPTION_DATA_TYPES = [
-  # Grandfathered types:
-  'UNSPECIFIED',  # Doesn't have a root tag or notification type.
-  'AUTOFILL_WALLET_DATA',  # Root tag and data type string lack DATA suffix.
-  'APP_SETTINGS',  # Data type string has inconsistent capitalization.
-  'EXTENSION_SETTINGS',  # Data type string has inconsistent capitalization.
-  'PROXY_TABS',  # Doesn't have a root tag or notification type.
-  'NIGORI',  # Data type string is 'encryption keys'.
-  'SUPERVISED_USER_SETTINGS',  # Root tag and data type string replace
-                               # 'Supervised' with 'Managed'
-
-  # Deprecated types:
-  'DEPRECATED_SUPERVISED_USER_ALLOWLISTS']
-
-# Root tags are used as prefixes when creating storage keys, so certain strings
-# are blocklisted in order to prevent prefix collision.
-BLOCKLISTED_ROOT_TAGS = [
-  '_mts_schema_descriptor'
-]
-
-# Number of distinct fields in a map entry; used to create
-# sets that check for uniqueness.
-MAP_ENTRY_FIELD_COUNT = 6
-
-# String that precedes the DataType when referencing the
-# proto field number enum e.g.
-# sync_pb::EntitySpecifics::kManagedUserFieldNumber.
-# Used to map from enum references to the DataType.
-FIELD_NUMBER_PREFIX = 'sync_pb::EntitySpecifics::k'
-
-# Start and end regexes for finding the EntitySpecifics definition in
-# entity_specifics.proto.
-PROTO_DEFINITION_START_PATTERN = '^  oneof specifics_variant \{'
-PROTO_DEFINITION_END_PATTERN = '^\}'
-
-# Start and end regexes for finding the DataTypeInfoMap definition
-# in data_type.cc.
-DATA_TYPE_START_PATTERN = '^const DataTypeInfo kDataTypeInfoMap'
-DATA_TYPE_END_PATTERN = '^\};'
-
-# Strings relating to files we'll need to read.
-# data_type.cc is where the DataTypeInfoMap is
-# entity_specifics.proto is where the proto definitions for DataTypes are.
-ENTITY_SPECIFICS_PROTO_FILE_PATH = './protocol/entity_specifics.proto'
-ENTITY_SPECIFICS_PROTO_FILE_NAME = 'entity_specifics.proto'
-DATA_TYPE_FILE_NAME = 'data_type.cc'
-
-SYNC_SOURCE_FILES = (r'^components[\\/]sync[\\/].*\.(cc|h)$',)
-
-def CheckDataTypeInfoMap(input_api, output_api, data_type_file):
-  """Checks the kDataTypeInfoMap in data_type.cc follows conventions.
-  Checks that the kDataTypeInfoMap follows the below rules:
-    1) The data type string should match the data type name, but with
-       only the first letter capitalized and spaces instead of underscores.
-    2) The root tag should be the same as the data type but all lowercase.
-    3) The notification type should match the proto message name.
-    4) No duplicate data across data types.
-   Args:
-    input_api: presubmit_support InputApi instance
-    output_api: presubmit_support OutputApi instance
-    data_type_file: AffectedFile object where the DataTypeInfoMap is
-  Returns:
-    A (potentially empty) list PresubmitError objects corresponding to
-    violations of the above rules.
-  """
-  accumulated_problems = []
-  map_entries = ParseDataTypeEntries(
-    input_api, data_type_file.AbsoluteLocalPath())
-  # If any line of the map changed, we check the whole thing since
-  # definitions span multiple lines and there are rules that apply across
-  # all definitions e.g. no duplicated field values.
-  check_map = False
-  for line_num, _ in data_type_file.ChangedContents():
-    for map_entry in map_entries:
-      if line_num in map_entry.affected_lines:
-        check_map = True
-        break
-
-  if not check_map:
-    return []
-  proto_field_definitions = ParseEntitySpecificsProtoFieldIdentifiers(
-    input_api, os.path.abspath(ENTITY_SPECIFICS_PROTO_FILE_PATH))
-  accumulated_problems.extend(
-    CheckNoDuplicatedFieldValues(output_api, map_entries))
-
-  for map_entry in map_entries:
-    entry_problems = []
-    entry_problems.extend(
-      CheckNotificationTypeMatchesProtoMessageName(
-        output_api, map_entry, proto_field_definitions))
-    entry_problems.extend(CheckRootTagNotInBlocklist(output_api, map_entry))
-
-    if map_entry.data_type not in EXCEPTION_DATA_TYPES:
-      entry_problems.extend(
-        CheckDataTypeStringMatchesDataType(output_api, map_entry))
-      entry_problems.extend(
-        CheckRootTagMatchesDataType(output_api, map_entry))
-
-    if len(entry_problems) > 0:
-      accumulated_problems.extend(entry_problems)
-
-  return accumulated_problems
-
-
-class DataTypeEnumEntry(object):
-  """Class that encapsulates a DataTypeInfo definition in data_type.cc.
-  Allows access to each of the named fields in the definition and also
-  which lines the definition spans.
-  Attributes:
-    data_type: entry's DataType enum value
-    notification_type: data type's notification string
-    root_tag: data type's root tag
-    data_type_string: string corresponding to the DataType
-    field_number: proto field number
-    histogram_val: value identifying DataType in histogram
-    affected_lines: lines in data_type.cc that the definition spans
-  """
-  def __init__(self, entry_strings, affected_lines):
-    (data_type, notification_type, root_tag, data_type_string,
-          field_number, histogram_val) = entry_strings
-    self.data_type = data_type
-    self.notification_type = notification_type
-    self.root_tag = root_tag
-    self.data_type_string = data_type_string
-    self.field_number = field_number
-    self.histogram_val = histogram_val
-    self.affected_lines = affected_lines
-
-
-def ParseDataTypeEntries(input_api, data_type_cc_path):
-  """Parses data_type_cc_path for DataTypeEnumEntries
-  Args:
-    input_api: presubmit_support InputAPI instance
-    data_type_cc_path: path to file containing the DataTypeInfo entries
-  Returns:
-    A list of DataTypeEnumEntry objects read from data_type.cc.
-    e.g. ('AUTOFILL_WALLET_METADATA', 'WALLET_METADATA',
-      'autofill_wallet_metadata', 'Autofill Wallet Metadata',
-      'sync_pb::EntitySpecifics::kWalletMetadataFieldNumber', '35',
-      [63, 64, 65])
-  """
-  file_contents = input_api.ReadFile(data_type_cc_path)
-  start_pattern = input_api.re.compile(DATA_TYPE_START_PATTERN)
-  end_pattern = input_api.re.compile(DATA_TYPE_END_PATTERN)
-  results, definition_strings, definition_lines = [], [], []
-  inside_enum = False
-  current_line_number = 0
-  for line in file_contents.splitlines():
-    current_line_number += 1
-    if line.strip().startswith('//'):
-      # Ignore comments.
-      continue
-    if start_pattern.match(line):
-      inside_enum = True
-      continue
-    if inside_enum:
-      if end_pattern.match(line):
-        break
-      line_entries = line.strip().strip('{},').split(',')
-      definition_strings.extend([entry.strip('" ') for entry in line_entries])
-      definition_lines.append(current_line_number)
-      if line.endswith('},'):
-        results.append(DataTypeEnumEntry(definition_strings, definition_lines))
-        definition_strings = []
-        definition_lines = []
-  return results
-
-
-def ParseEntitySpecificsProtoFieldIdentifiers(input_api, proto_path):
-  """Parses proto field identifiers from the EntitySpecifics definition.
-  Args:
-    input_api: presubmit_support InputAPI instance
-    proto_path: path to the file containing the proto field definitions
-  Returns:
-    A dictionary of the format {'SyncDataType': 'field_identifier'}
-    e.g. {'AutofillSpecifics': 'autofill'}
-  """
-  proto_field_definitions = {}
-  proto_file_contents = input_api.ReadFile(proto_path).splitlines()
-  start_pattern = input_api.re.compile(PROTO_DEFINITION_START_PATTERN)
-  end_pattern = input_api.re.compile(PROTO_DEFINITION_END_PATTERN)
-  in_proto_def = False
-  split_proto_line = []
-  for line in proto_file_contents:
-    if start_pattern.match(line):
-      in_proto_def = True
-      continue
-    if in_proto_def:
-      if end_pattern.match(line):
-        break
-      # Clean up last line on the end of the definition.
-      if len(split_proto_line) > 0 and (';' in split_proto_line[-1]):
-        split_proto_line.clear()
-      line = line.strip()
-      # Ignore comments.
-      if '//' in line:
-        continue
-      split_proto_line.extend(line.split(' '))
-      # Ignore lines that don't contain definitions.
-      if len(split_proto_line) < 3:
-        continue
-
-      field_typename = split_proto_line[0]
-      field_identifier = split_proto_line[1]
-      proto_field_definitions[field_typename] = field_identifier
-  return proto_field_definitions
-
-def StripTrailingS(string):
-  return string.rstrip('sS')
-
-
-def IsTitleCased(string):
-  return all([s[0].isupper() for s in string.split(' ')])
-
-
-def FormatPresubmitError(output_api, message, affected_lines):
-  """ Outputs a formatted error message with filename and line number(s).
-  """
-  if len(affected_lines) > 1:
-    message_including_lines = 'Error at lines %d-%d in data_type.cc: %s' %(
-      affected_lines[0], affected_lines[-1], message)
-  else:
-    message_including_lines = 'Error at line %d in data_type.cc: %s' %(
-      affected_lines[0], message)
-  return output_api.PresubmitError(message_including_lines)
-
-
-def CheckNotificationTypeMatchesProtoMessageName(
-  output_api, map_entry, proto_field_definitions):
-  """Check that map_entry's notification type matches entity_specifics.proto.
-  Verifies that the notification_type matches the name of the field defined
-  in the entity_specifics.proto by looking it up in the proto_field_definitions
-  map.
-  Args:
-    output_api: presubmit_support OutputApi instance
-    map_entry: DataTypeEnumEntry instance
-    proto_field_definitions: dict of proto field types and field names
-  Returns:
-    A potentially empty list of PresubmitError objects corresponding to
-    violations of the above rule
-  """
-  if map_entry.field_number == '-1':
-    return []
-  proto_message_name = proto_field_definitions[
-    FieldNumberToPrototypeString(map_entry.field_number)]
-  if map_entry.notification_type.lower() != proto_message_name:
-    return [
-      FormatPresubmitError(
-        output_api,'In the construction of DataTypeInfo: notification type'
-        ' "%s" does not match proto message'
-        ' name defined in entity_specifics.proto: ' '"%s"' %
-        (map_entry.notification_type, proto_message_name),
-        map_entry.affected_lines)]
-  return []
-
-
-def CheckNoDuplicatedFieldValues(output_api, map_entries):
-  """Check that map_entries has no duplicated field values.
-  Verifies that every map_entry in map_entries doesn't have a field value
-  used elsewhere in map_entries, ignoring special values ("" and -1).
-  Args:
-    output_api: presubmit_support OutputApi instance
-    map_entries: list of DataTypeEnumEntry objects to check
-  Returns:
-    A list PresubmitError objects for each duplicated field value
-  """
-  problem_list = []
-  field_value_sets = [set() for i in range(MAP_ENTRY_FIELD_COUNT)]
-  for map_entry in map_entries:
-    field_values = [
-      map_entry.data_type, map_entry.notification_type,
-      map_entry.root_tag, map_entry.data_type_string,
-      map_entry.field_number, map_entry.histogram_val]
-    for i in range(MAP_ENTRY_FIELD_COUNT):
-      field_value = field_values[i]
-      field_value_set = field_value_sets[i]
-      if field_value in field_value_set:
-        problem_list.append(
-          FormatPresubmitError(
-            output_api, 'Duplicated field value "%s"' % field_value,
-            map_entry.affected_lines))
-      elif len(field_value) > 0 and field_value != '-1':
-        field_value_set.add(field_value)
-  return problem_list
-
-
-def CheckDataTypeStringMatchesDataType(output_api, map_entry):
-  """Check that map_entry's data_type_string matches DataType.
-  Args:
-    output_api: presubmit_support OutputApi instance
-    map_entry: DataTypeEnumEntry object to check
-  Returns:
-    A list of PresubmitError objects for each violation
-  """
-  problem_list = []
-  expected_data_type_string = map_entry.data_type.lower().replace('_', ' ')
-  if (StripTrailingS(expected_data_type_string) !=
-    StripTrailingS(map_entry.data_type_string.lower())):
-    problem_list.append(
-      FormatPresubmitError(
-        output_api,'data type string "%s" does not match data type.'
-        ' It should be "%s"' % (
-          map_entry.data_type_string, expected_data_type_string.title()),
-        map_entry.affected_lines))
-  if not IsTitleCased(map_entry.data_type_string):
-    problem_list.append(
-      FormatPresubmitError(
-        output_api,'data type string "%s" should be title cased' %
-        (map_entry.data_type_string), map_entry.affected_lines))
-  return problem_list
-
-
-def CheckRootTagMatchesDataType(output_api, map_entry):
-  """Check that map_entry's root tag matches DataType.
-  Args:
-    output_api: presubmit_support OutputAPI instance
-    map_entry: DataTypeEnumEntry object to check
-  Returns:
-    A list of PresubmitError objects for each violation
-  """
-  expected_root_tag = map_entry.data_type.lower()
-  if (StripTrailingS(expected_root_tag) !=
-    StripTrailingS(map_entry.root_tag)):
-    return [
-      FormatPresubmitError(
-        output_api,'root tag "%s" does not match data type. It should '
-        'be "%s"' % (map_entry.root_tag, expected_root_tag),
-        map_entry.affected_lines)]
-  return []
-
-def CheckRootTagNotInBlocklist(output_api, map_entry):
-  """ Checks that map_entry's root isn't a blocklisted string.
-  Args:
-    output_api: presubmit_support OutputAPI instance
-    map_entry: DataTypeEnumEntry object to check
-  Returns:
-    A list of PresubmitError objects for each violation
-  """
-  if map_entry.root_tag in BLOCKLISTED_ROOT_TAGS:
-    return [FormatPresubmitError(
-        output_api,'root tag "%s" is a blocklisted root tag'
-        % (map_entry.root_tag), map_entry.affected_lines)]
-  return []
-
-
-def FieldNumberToPrototypeString(field_number):
-  """Converts a field number enum reference to an EntitySpecifics string.
-  Converts a reference to the field number enum to the corresponding
-  proto data type string.
-  Args:
-    field_number: string representation of a field number enum reference
-  Returns:
-    A string that is the corresponding proto field data type. e.g.
-    FieldNumberToPrototypeString('EntitySpecifics::kAppFieldNumber')
-    => 'AppSpecifics'
-  """
-  return field_number.replace(FIELD_NUMBER_PREFIX, '').replace(
-    'FieldNumber', 'Specifics')
-
-def CheckChangeLintsClean(input_api, output_api):
-  source_filter = lambda x: input_api.FilterSourceFile(
-    x, files_to_check=SYNC_SOURCE_FILES, files_to_skip=None)
-  return input_api.canned_checks.CheckChangeLintsClean(
-      input_api, output_api, source_filter, lint_filters=[], verbose_level=1)
-
-def CheckChanges(input_api, output_api):
-  results = []
-  results += CheckChangeLintsClean(input_api, output_api)
-
-  proto_file_changed = False
-  proto_visitors_changed = False
-
-  for f in input_api.AffectedFiles():
-    if (f.LocalPath().endswith(DATA_TYPE_FILE_NAME) or
-        f.LocalPath().endswith(ENTITY_SPECIFICS_PROTO_FILE_NAME)):
-      results += CheckDataTypeInfoMap(input_api, output_api, f)
-
-    if (f.LocalPath().endswith('.proto')):
-      proto_file_changed = True
-    if (f.LocalPath().endswith(os.path.sep + 'proto_visitors.h')):
-      proto_visitors_changed = True
-
-  if proto_file_changed and not proto_visitors_changed:
-    results.append(
-      output_api.PresubmitPromptWarning(
-        'You changed proto files, but didn\'t change proto_visitors.h'))
-
-  return results
-
-def CheckChangeOnUpload(input_api, output_api):
-  return CheckChanges(input_api, output_api)
-
-def CheckChangeOnCommit(input_api, output_api):
-  return CheckChanges(input_api, output_api)
diff --git a/components/sync/PRESUBMIT_test.py b/components/sync/PRESUBMIT_test.py
deleted file mode 100755
index 2e880e48..0000000
--- a/components/sync/PRESUBMIT_test.py
+++ /dev/null
@@ -1,206 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-import os
-import re
-import sys
-import unittest
-
-import PRESUBMIT
-
-sys.path.append(
-  os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
-from PRESUBMIT_test_mocks import MockChange, MockOutputApi
-
-class MockCannedChecks(object):
-  def CheckChangeLintsClean(self, input_api, output_api, source_filter,
-                            lint_filters, verbose_level):
-    return []
-
-
-class MockInputApi(object):
-  """ Mocked input api for unit testing of presubmit.
-  This lets us mock things like file system operations and changed files.
-  """
-  def __init__(self):
-    self.canned_checks = MockCannedChecks()
-    self.re = re
-    self.os_path = os.path
-    self.files = []
-    self.is_committing = False
-
-  def AffectedFiles(self):
-    return self.files
-
-  def AffectedSourceFiles(self):
-    return self.files
-
-  def ReadFile(self, f):
-    """ Returns the mock contents of f if they've been defined.
-    """
-    for api_file in self.files:
-        if api_file.LocalPath() == f:
-            return api_file.NewContents()
-
-
-class MockFile(object):
-  """Mock file object so that presubmit can act invoke file system operations.
-  """
-  def __init__(self, local_path, new_contents):
-    self._local_path = local_path
-    self._new_contents = new_contents
-    self._changed_contents = ([(i + 1, l) for i, l in enumerate(new_contents)])
-
-  def ChangedContents(self):
-    return self._changed_contents
-
-  def NewContents(self):
-    return self._new_contents
-
-  def LocalPath(self):
-    return self._local_path
-
-  def AbsoluteLocalPath(self):
-    return self._local_path
-
-
-# Format string used as the contents of a mock sync.proto in order to
-# test presubmit parsing of EntitySpecifics definition in that file.
-MOCK_PROTOFILE_CONTENTS = ('\n'
-  'message EntitySpecifics {\n'
-  '  //comment\n'
-  '\n'
-  '  oneof specifics_variant {\n'
-  '    AutofillSpecifics autofill = 123;\n'
-  '    AppSpecifics app = 456;\n'
-  '    AppSettingSpecifics app_setting = 789;\n'
-  '    ExtensionSettingSpecifics extension_setting = 910;\n'
-  '    //comment\n'
-  '  }\n'
-  '}\n'
-  )
-
-
-# Format string used as the contents of a mock data_type.cc
-# in order to test presubmit parsing of the DataTypeInfoMap in that file.
-MOCK_DATATYPE_CONTENTS =('\n'
-  'const DataTypeInfo kDataTypeInfoMap[] = {\n'
-  '// Some comment \n'
-  '{APP_SETTINGS, "APP_SETTING", "app_settings", "App settings",\n'
-  'sync_pb::EntitySpecifics::kAppSettingFieldNumber, 13},\n'
-  '%s\n'
-  '};\n')
-
-
-class DataTypeInfoChangeTest(unittest.TestCase):
-  """Unit testing class that contains tests for sync/PRESUBMIT.py.
-  """
-  def test_ValidChangeMultiLine(self):
-    results = self._testChange('{APPS, "APP", "apps", "Apps",\n'
-     'sync_pb::EntitySpecifics::kAppFieldNumber, 12},')
-    self.assertEqual(0, len(results))
-
-  def testValidChangeToleratesPluralization(self):
-    results = self._testChange('{APPS, "APP", "apps", "App",\n'
-      'sync_pb::EntitySpecifics::kAppFieldNumber, 12},')
-    self.assertEqual(0, len(results))
-
-  def testValidChangeGrandfatheredEntry(self):
-    results = self._testChange('{PROXY_TABS, "", "", "Tabs", -1, 25},')
-    self.assertEqual(0, len(results))
-
-  # TODO(crbug.com/40744701): The only remaining deprecated type doesn't satisfy
-  # this test, revisit it.
-  def DISABLED_testValidChangeDeprecatedEntry(self):
-    results = self._testChange('{DEPRECATED_SUPERVISED_USER_ALLOWLISTS,\n'
-      '"MANAGED_USER_WHITELIST",\n'
-      '"managed_user_whitelists", "Managed User Whitelists",\n'
-      'sync_pb::EntitySpecifics::kManagedUserWhitelistFieldNumber, 33},')
-    self.assertEqual(0, len(results))
-
-  def testInvalidChangeMismatchedNotificationType(self):
-    results = self._testChange('{AUTOFILL, "AUTOFILL_WRONG", "autofill",\n'
-     '"Autofill",sync_pb::EntitySpecifics::kAutofillFieldNumber, 6},')
-    self.assertEqual(1, len(results))
-    self.assertTrue('notification type' in results[0].message)
-
-  def testInvalidChangeInconsistentDataType(self):
-    results = self._testChange('{AUTOFILL, "AUTOFILL", "autofill",\n'
-     '"Autofill Extra",sync_pb::EntitySpecifics::kAutofillFieldNumber, 6},')
-    self.assertEqual(1, len(results))
-    self.assertTrue('data type string' in results[0].message)
-
-  def testInvalidChangeNotTitleCased(self):
-    results = self._testChange('{AUTOFILL, "AUTOFILL", "autofill",\n'
-     '"autofill",sync_pb::EntitySpecifics::kAutofillFieldNumber, 6},')
-    self.assertEqual(1, len(results))
-    self.assertTrue('title' in results[0].message)
-
-  def testInvalidChangeInconsistentRootTag(self):
-    results = self._testChange('{AUTOFILL, "AUTOFILL", "autofill root",\n'
-     '"Autofill",sync_pb::EntitySpecifics::kAutofillFieldNumber, 6},')
-    self.assertEqual(1, len(results))
-    self.assertTrue('root tag' in results[0].message)
-
-  def testInvalidChangeDuplicatedValues(self):
-    results = self._testChange('{APP_SETTINGS, "APP_SETTING",\n'
-      '"app_settings", "App settings",\n'
-      'sync_pb::EntitySpecifics::kAppSettingFieldNumber, 13},\n')
-    self.assertEqual(6, len(results))
-    self.assertTrue('APP_SETTINGS' in results[0].message)
-
-  def testBlocklistedRootTag(self):
-    results = self._testChange('{EXTENSION_SETTING, "EXTENSION_SETTING",\n'
-      '"_mts_schema_descriptor","Extension Setting",\n'
-      'sync_pb::EntitySpecifics::kExtensionSettingFieldNumber, 6},')
-    self.assertEqual(2, len(results))
-    self.assertTrue('_mts_schema_descriptor' in results[0].message)
-    self.assertTrue("blocklist" in results[0].message)
-
-  def testProtoChangeWithoutVisitors(self):
-    files = [
-      MockFile(os.path.abspath('./protocol/entity_specifics.proto'), '')
-    ]
-    results = self._testChangeWithFiles(files)
-    # Changing a .proto file without also updating proto_visitors.h should
-    # result in a warning.
-    self.assertEqual(1, len(results))
-    self.assertTrue("proto_visitors.h" in results[0].message)
-
-  def testProtoChangeWithVisitors(self):
-    files = [
-      MockFile(os.path.abspath('./protocol/entity_specifics.proto'), ''),
-      MockFile(os.path.abspath('./protocol/proto_visitors.h'), '')
-    ]
-    results = self._testChangeWithFiles(files)
-    # Changing .proto files along with proto_visitors.h is good.
-    self.assertEqual(0, len(results))
-
-  def testProtoVisitorsChange(self):
-    files = [
-      MockFile(os.path.abspath('./protocol/proto_visitors.h'), '')
-    ]
-    results = self._testChangeWithFiles(files)
-    # Changing proto_visitors.h without changing any proto files is fine.
-    self.assertEqual(0, len(results))
-
-  def _testChangeWithFiles(self, files):
-    mock_input_api = MockInputApi()
-    mock_input_api.files = files
-    return PRESUBMIT.CheckChangeOnCommit(mock_input_api, MockOutputApi())
-
-  def _testChange(self, datatype_literal):
-    files = [
-      MockFile(os.path.abspath('./protocol/entity_specifics.proto'),
-        MOCK_PROTOFILE_CONTENTS),
-      MockFile(os.path.abspath('./protocol/proto_visitors.h'), ''),
-      MockFile(os.path.abspath('./base/data_type.cc'),
-        MOCK_DATATYPE_CONTENTS % (datatype_literal))
-    ]
-    return self._testChangeWithFiles(files)
-
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/components/sync/base/data_type.cc b/components/sync/base/data_type.cc
index 6a181c2..e399b52f 100644
--- a/components/sync/base/data_type.cc
+++ b/components/sync/base/data_type.cc
@@ -15,221 +15,6 @@
 
 namespace {
 
-struct DataTypeInfo {
-  const DataType data_type = UNSPECIFIED;
-  // Used to identify the data type in the SyncDataType histogram_suffix in
-  // histograms.xml. Must always be kept in sync.
-  const char* const histogram_suffix = nullptr;
-  // Root tag for Data Type
-  // This should be the same as the data type but all lowercase.
-  const char* const lowercase_root_tag = nullptr;
-  // String value for Data Type
-  // This should be the same as the data type but space separated and the
-  // first letter of every word capitalized.
-  const char* const data_type_debug_string = nullptr;
-  // Field number of the data type specifics in EntitySpecifics.
-  const int specifics_field_number = -1;
-  // Data type value from SyncDataTypes enum in enums.xml. Must always be in
-  // sync with the enum.
-  const DataTypeForHistograms data_type_histogram_val =
-      DataTypeForHistograms::kUnspecified;
-};
-
-// Below struct entries are in the same order as their definition in the
-// DataType enum. When making changes to this list, don't forget to
-//  - update kSpecificsFieldNumberToDataTypeMap below,
-//  - update the DataType enum,
-//  - update the SyncDataTypes enum in enums.xml, and
-//  - update the SyncDataType histogram suffix in histograms.xml.
-// Struct field values should be unique across the entire map.
-// LINT.IfChange(DataTypeHistogramSuffix)
-constexpr auto kDataTypeInfoMap = std::to_array<DataTypeInfo>(
-    {{UNSPECIFIED, "", "", "Unspecified", -1,
-      DataTypeForHistograms::kUnspecified},
-     {BOOKMARKS, "BOOKMARK", "bookmarks", "Bookmarks",
-      sync_pb::EntitySpecifics::kBookmarkFieldNumber,
-      DataTypeForHistograms::kBookmarks},
-     {PREFERENCES, "PREFERENCE", "preferences", "Preferences",
-      sync_pb::EntitySpecifics::kPreferenceFieldNumber,
-      DataTypeForHistograms::kPreferences},
-     {PASSWORDS, "PASSWORD", "passwords", "Passwords",
-      sync_pb::EntitySpecifics::kPasswordFieldNumber,
-      DataTypeForHistograms::kPasswords},
-     {AUTOFILL_PROFILE, "AUTOFILL_PROFILE", "autofill_profiles",
-      "Autofill Profiles",
-      sync_pb::EntitySpecifics::kAutofillProfileFieldNumber,
-      DataTypeForHistograms::kAutofillProfile},
-     {AUTOFILL, "AUTOFILL", "autofill", "Autofill",
-      sync_pb::EntitySpecifics::kAutofillFieldNumber,
-      DataTypeForHistograms::kAutofill},
-     {AUTOFILL_WALLET_CREDENTIAL, "AUTOFILL_WALLET_CREDENTIAL",
-      "autofill_wallet_credential", "Autofill Wallet Credential",
-      sync_pb::EntitySpecifics::kAutofillWalletCredentialFieldNumber,
-      DataTypeForHistograms::kAutofillWalletCredential},
-     {AUTOFILL_WALLET_DATA, "AUTOFILL_WALLET", "autofill_wallet",
-      "Autofill Wallet", sync_pb::EntitySpecifics::kAutofillWalletFieldNumber,
-      DataTypeForHistograms::kAutofillWalletData},
-     {AUTOFILL_WALLET_METADATA, "WALLET_METADATA", "autofill_wallet_metadata",
-      "Autofill Wallet Metadata",
-      sync_pb::EntitySpecifics::kWalletMetadataFieldNumber,
-      DataTypeForHistograms::kAutofillWalletMetadata},
-     {AUTOFILL_WALLET_OFFER, "AUTOFILL_OFFER", "autofill_wallet_offer",
-      "Autofill Wallet Offer",
-      sync_pb::EntitySpecifics::kAutofillOfferFieldNumber,
-      DataTypeForHistograms::kAutofillWalletOffer},
-     {AUTOFILL_WALLET_USAGE, "AUTOFILL_WALLET_USAGE", "autofill_wallet_usage",
-      "Autofill Wallet Usage",
-      sync_pb::EntitySpecifics::kAutofillWalletUsageFieldNumber,
-      DataTypeForHistograms::kAutofillWalletUsage},
-     {THEMES, "THEME", "themes", "Themes",
-      sync_pb::EntitySpecifics::kThemeFieldNumber,
-      DataTypeForHistograms::kThemes},
-     {EXTENSIONS, "EXTENSION", "extensions", "Extensions",
-      sync_pb::EntitySpecifics::kExtensionFieldNumber,
-      DataTypeForHistograms::kExtensions},
-     {SEARCH_ENGINES, "SEARCH_ENGINE", "search_engines", "Search Engines",
-      sync_pb::EntitySpecifics::kSearchEngineFieldNumber,
-      DataTypeForHistograms::kSearchEngines},
-     {SESSIONS, "SESSION", "sessions", "Sessions",
-      sync_pb::EntitySpecifics::kSessionFieldNumber,
-      DataTypeForHistograms::kSessions},
-     {APPS, "APP", "apps", "Apps", sync_pb::EntitySpecifics::kAppFieldNumber,
-      DataTypeForHistograms::kApps},
-     {APP_SETTINGS, "APP_SETTING", "app_settings", "App settings",
-      sync_pb::EntitySpecifics::kAppSettingFieldNumber,
-      DataTypeForHistograms::kAppSettings},
-     {EXTENSION_SETTINGS, "EXTENSION_SETTING", "extension_settings",
-      "Extension settings",
-      sync_pb::EntitySpecifics::kExtensionSettingFieldNumber,
-      DataTypeForHistograms::kExtensionSettings},
-     {HISTORY_DELETE_DIRECTIVES, "HISTORY_DELETE_DIRECTIVE",
-      "history_delete_directives", "History Delete Directives",
-      sync_pb::EntitySpecifics::kHistoryDeleteDirectiveFieldNumber,
-      DataTypeForHistograms::kHistoryDeleteDirectices},
-     {DICTIONARY, "DICTIONARY", "dictionary", "Dictionary",
-      sync_pb::EntitySpecifics::kDictionaryFieldNumber,
-      DataTypeForHistograms::kDictionary},
-     {DEVICE_INFO, "DEVICE_INFO", "device_info", "Device Info",
-      sync_pb::EntitySpecifics::kDeviceInfoFieldNumber,
-      DataTypeForHistograms::kDeviceInfo},
-     {PRIORITY_PREFERENCES, "PRIORITY_PREFERENCE", "priority_preferences",
-      "Priority Preferences",
-      sync_pb::EntitySpecifics::kPriorityPreferenceFieldNumber,
-      DataTypeForHistograms::kPriorityPreferences},
-     {SUPERVISED_USER_SETTINGS, "MANAGED_USER_SETTING", "managed_user_settings",
-      "Managed User Settings",
-      sync_pb::EntitySpecifics::kManagedUserSettingFieldNumber,
-      DataTypeForHistograms::kSupervisedUserSettings},
-     {APP_LIST, "APP_LIST", "app_list", "App List",
-      sync_pb::EntitySpecifics::kAppListFieldNumber,
-      DataTypeForHistograms::kAppList},
-     {ARC_PACKAGE, "ARC_PACKAGE", "arc_package", "Arc Package",
-      sync_pb::EntitySpecifics::kArcPackageFieldNumber,
-      DataTypeForHistograms::kArcPackage},
-     {PRINTERS, "PRINTER", "printers", "Printers",
-      sync_pb::EntitySpecifics::kPrinterFieldNumber,
-      DataTypeForHistograms::kPrinters},
-     {READING_LIST, "READING_LIST", "reading_list", "Reading List",
-      sync_pb::EntitySpecifics::kReadingListFieldNumber,
-      DataTypeForHistograms::kReadingList},
-     {USER_EVENTS, "USER_EVENT", "user_events", "User Events",
-      sync_pb::EntitySpecifics::kUserEventFieldNumber,
-      DataTypeForHistograms::kUserEvents},
-     {USER_CONSENTS, "USER_CONSENT", "user_consent", "User Consents",
-      sync_pb::EntitySpecifics::kUserConsentFieldNumber,
-      DataTypeForHistograms::kUserConsents},
-     {SEND_TAB_TO_SELF, "SEND_TAB_TO_SELF", "send_tab_to_self",
-      "Send Tab To Self", sync_pb::EntitySpecifics::kSendTabToSelfFieldNumber,
-      DataTypeForHistograms::kSendTabToSelf},
-     {SECURITY_EVENTS, "SECURITY_EVENT", "security_events", "Security Events",
-      sync_pb::EntitySpecifics::kSecurityEventFieldNumber,
-      DataTypeForHistograms::kSecurityEvents},
-     {WIFI_CONFIGURATIONS, "WIFI_CONFIGURATION", "wifi_configurations",
-      "Wifi Configurations",
-      sync_pb::EntitySpecifics::kWifiConfigurationFieldNumber,
-      DataTypeForHistograms::kWifiConfigurations},
-     {WEB_APPS, "WEB_APP", "web_apps", "Web Apps",
-      sync_pb::EntitySpecifics::kWebAppFieldNumber,
-      DataTypeForHistograms::kWebApps},
-     {WEB_APKS, "WEB_APK", "web_apks", "Web Apks",
-      sync_pb::EntitySpecifics::kWebApkFieldNumber,
-      DataTypeForHistograms::kWebApks},
-     {OS_PREFERENCES, "OS_PREFERENCE", "os_preferences", "OS Preferences",
-      sync_pb::EntitySpecifics::kOsPreferenceFieldNumber,
-      DataTypeForHistograms::kOsPreferences},
-     {OS_PRIORITY_PREFERENCES, "OS_PRIORITY_PREFERENCE",
-      "os_priority_preferences", "OS Priority Preferences",
-      sync_pb::EntitySpecifics::kOsPriorityPreferenceFieldNumber,
-      DataTypeForHistograms::kOsPriorityPreferences},
-     {SHARING_MESSAGE, "SHARING_MESSAGE", "sharing_message", "Sharing Message",
-      sync_pb::EntitySpecifics::kSharingMessageFieldNumber,
-      DataTypeForHistograms::kSharingMessage},
-     {WORKSPACE_DESK, "WORKSPACE_DESK", "workspace_desk", "Workspace Desk",
-      sync_pb::EntitySpecifics::kWorkspaceDeskFieldNumber,
-      DataTypeForHistograms::kWorkspaceDesk},
-     {HISTORY, "HISTORY", "history", "History",
-      sync_pb::EntitySpecifics::kHistoryFieldNumber,
-      DataTypeForHistograms::kHistory},
-     {PRINTERS_AUTHORIZATION_SERVERS, "PRINTERS_AUTHORIZATION_SERVER",
-      "printers_authorization_servers", "Printers Authorization Servers",
-      sync_pb::EntitySpecifics::kPrintersAuthorizationServerFieldNumber,
-      DataTypeForHistograms::kPrintersAuthorizationServers},
-     {CONTACT_INFO, "CONTACT_INFO", "contact_info", "Contact Info",
-      sync_pb::EntitySpecifics::kContactInfoFieldNumber,
-      DataTypeForHistograms::kContactInfo},
-     {SAVED_TAB_GROUP, "SAVED_TAB_GROUP", "saved_tab_group", "Saved Tab Group",
-      sync_pb::EntitySpecifics::kSavedTabGroupFieldNumber,
-      DataTypeForHistograms::kSavedTabGroups},
-     {POWER_BOOKMARK, "POWER_BOOKMARK", "power_bookmark", "Power Bookmark",
-      sync_pb::EntitySpecifics::kPowerBookmarkFieldNumber,
-      DataTypeForHistograms::kPowerBookmark},
-     {WEBAUTHN_CREDENTIAL, "WEBAUTHN_CREDENTIAL", "webauthn_credential",
-      "WebAuthn Credentials",
-      sync_pb::EntitySpecifics::kWebauthnCredentialFieldNumber,
-      DataTypeForHistograms::kWebAuthnCredentials},
-     {INCOMING_PASSWORD_SHARING_INVITATION,
-      "INCOMING_PASSWORD_SHARING_INVITATION",
-      "incoming_password_sharing_invitation",
-      "Incoming Password Sharing Invitations",
-      sync_pb::EntitySpecifics::kIncomingPasswordSharingInvitationFieldNumber,
-      DataTypeForHistograms::kIncomingPasswordSharingInvitations},
-     {OUTGOING_PASSWORD_SHARING_INVITATION,
-      "OUTGOING_PASSWORD_SHARING_INVITATION",
-      "outgoing_password_sharing_invitation",
-      "Outgoing Password Sharing Invitations",
-      sync_pb::EntitySpecifics::kOutgoingPasswordSharingInvitationFieldNumber,
-      DataTypeForHistograms::kOutgoingPasswordSharingInvitations},
-     {SHARED_TAB_GROUP_DATA, "SHARED_TAB_GROUP_DATA", "shared_tab_group_data",
-      "Shared Tab Group Data",
-      sync_pb::EntitySpecifics::kSharedTabGroupDataFieldNumber,
-      DataTypeForHistograms::kSharedTabGroupData},
-     {COLLABORATION_GROUP, "COLLABORATION_GROUP", "collaboration_group",
-      "Collaboration Group",
-      sync_pb::EntitySpecifics::kCollaborationGroupFieldNumber,
-      DataTypeForHistograms::kCollaborationGroup},
-     {PLUS_ADDRESS, "PLUS_ADDRESS", "plus_address", "Plus Address",
-      sync_pb::EntitySpecifics::kPlusAddressFieldNumber,
-      DataTypeForHistograms::kPlusAddresses},
-     {PRODUCT_COMPARISON, "PRODUCT_COMPARISON", "product_comparison",
-      "Product Comparison",
-      sync_pb::EntitySpecifics::kProductComparisonFieldNumber,
-      DataTypeForHistograms::kProductComparison},
-     {COOKIES, "COOKIE", "cookies", "Cookies",
-      sync_pb::EntitySpecifics::kCookieFieldNumber,
-      DataTypeForHistograms::kCookies},
-     {PLUS_ADDRESS_SETTING, "PLUS_ADDRESS_SETTING", "plus_address_setting",
-      "Plus Address Setting",
-      sync_pb::EntitySpecifics::kPlusAddressSettingFieldNumber,
-      DataTypeForHistograms::kPlusAddressSettings},
-     // ---- Control Types ----
-     {NIGORI, "NIGORI", "nigori", "Encryption Keys",
-      sync_pb::EntitySpecifics::kNigoriFieldNumber,
-      DataTypeForHistograms::kNigori}});
-// LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:DataTypeHistogramSuffix)
-
-static_assert(kDataTypeInfoMap.size() == GetNumDataTypes(),
-              "kDataTypeInfoMap should have GetNumDataTypes() elements");
-
 static_assert(53 == syncer::GetNumDataTypes(),
               "When adding a new type, update enum SyncDataTypes in enums.xml "
               "and suffix SyncDataType in histograms.xml.");
@@ -239,8 +24,7 @@
               "https://www.chromium.org/developers/design-documents/sync/"
               "integration-checklist/");
 
-// kSpecificsFieldNumberToDataTypeMap must exactly match the kDataTypeInfoMap,
-// so its size must be syncer::GetNumDataTypes().
+// kSpecificsFieldNumberToDataTypeMap must have size syncer::GetNumDataTypes().
 //
 // NOTE: size here acts as a static assert on the constraint above.
 using kSpecificsFieldNumberToDataTypeMap =
@@ -503,7 +287,117 @@
 int GetSpecificsFieldNumberFromDataType(DataType data_type) {
   DCHECK(ProtocolTypes().Has(data_type))
       << "Only protocol types have field values.";
-  return kDataTypeInfoMap[data_type].specifics_field_number;
+  switch (data_type) {
+    case UNSPECIFIED:
+      return -1;
+    case BOOKMARKS:
+      return sync_pb::EntitySpecifics::kBookmarkFieldNumber;
+    case PREFERENCES:
+      return sync_pb::EntitySpecifics::kPreferenceFieldNumber;
+    case PASSWORDS:
+      return sync_pb::EntitySpecifics::kPasswordFieldNumber;
+    case AUTOFILL_PROFILE:
+      return sync_pb::EntitySpecifics::kAutofillProfileFieldNumber;
+    case AUTOFILL:
+      return sync_pb::EntitySpecifics::kAutofillFieldNumber;
+    case AUTOFILL_WALLET_CREDENTIAL:
+      return sync_pb::EntitySpecifics::kAutofillWalletCredentialFieldNumber;
+    case AUTOFILL_WALLET_DATA:
+      return sync_pb::EntitySpecifics::kAutofillWalletFieldNumber;
+    case AUTOFILL_WALLET_METADATA:
+      return sync_pb::EntitySpecifics::kWalletMetadataFieldNumber;
+    case AUTOFILL_WALLET_OFFER:
+      return sync_pb::EntitySpecifics::kAutofillOfferFieldNumber;
+    case AUTOFILL_WALLET_USAGE:
+      return sync_pb::EntitySpecifics::kAutofillWalletUsageFieldNumber;
+    case THEMES:
+      return sync_pb::EntitySpecifics::kThemeFieldNumber;
+    case EXTENSIONS:
+      return sync_pb::EntitySpecifics::kExtensionFieldNumber;
+    case SEARCH_ENGINES:
+      return sync_pb::EntitySpecifics::kSearchEngineFieldNumber;
+    case SESSIONS:
+      return sync_pb::EntitySpecifics::kSessionFieldNumber;
+    case APPS:
+      return sync_pb::EntitySpecifics::kAppFieldNumber;
+    case APP_SETTINGS:
+      return sync_pb::EntitySpecifics::kAppSettingFieldNumber;
+    case EXTENSION_SETTINGS:
+      return sync_pb::EntitySpecifics::kExtensionSettingFieldNumber;
+    case HISTORY_DELETE_DIRECTIVES:
+      return sync_pb::EntitySpecifics::kHistoryDeleteDirectiveFieldNumber;
+    case DICTIONARY:
+      return sync_pb::EntitySpecifics::kDictionaryFieldNumber;
+    case DEVICE_INFO:
+      return sync_pb::EntitySpecifics::kDeviceInfoFieldNumber;
+    case PRIORITY_PREFERENCES:
+      return sync_pb::EntitySpecifics::kPriorityPreferenceFieldNumber;
+    case SUPERVISED_USER_SETTINGS:
+      return sync_pb::EntitySpecifics::kManagedUserSettingFieldNumber;
+    case APP_LIST:
+      return sync_pb::EntitySpecifics::kAppListFieldNumber;
+    case ARC_PACKAGE:
+      return sync_pb::EntitySpecifics::kArcPackageFieldNumber;
+    case PRINTERS:
+      return sync_pb::EntitySpecifics::kPrinterFieldNumber;
+    case READING_LIST:
+      return sync_pb::EntitySpecifics::kReadingListFieldNumber;
+    case USER_EVENTS:
+      return sync_pb::EntitySpecifics::kUserEventFieldNumber;
+    case USER_CONSENTS:
+      return sync_pb::EntitySpecifics::kUserConsentFieldNumber;
+    case SEND_TAB_TO_SELF:
+      return sync_pb::EntitySpecifics::kSendTabToSelfFieldNumber;
+    case SECURITY_EVENTS:
+      return sync_pb::EntitySpecifics::kSecurityEventFieldNumber;
+    case WIFI_CONFIGURATIONS:
+      return sync_pb::EntitySpecifics::kWifiConfigurationFieldNumber;
+    case WEB_APPS:
+      return sync_pb::EntitySpecifics::kWebAppFieldNumber;
+    case WEB_APKS:
+      return sync_pb::EntitySpecifics::kWebApkFieldNumber;
+    case OS_PREFERENCES:
+      return sync_pb::EntitySpecifics::kOsPreferenceFieldNumber;
+    case OS_PRIORITY_PREFERENCES:
+      return sync_pb::EntitySpecifics::kOsPriorityPreferenceFieldNumber;
+    case SHARING_MESSAGE:
+      return sync_pb::EntitySpecifics::kSharingMessageFieldNumber;
+    case WORKSPACE_DESK:
+      return sync_pb::EntitySpecifics::kWorkspaceDeskFieldNumber;
+    case HISTORY:
+      return sync_pb::EntitySpecifics::kHistoryFieldNumber;
+    case PRINTERS_AUTHORIZATION_SERVERS:
+      return sync_pb::EntitySpecifics::kPrintersAuthorizationServerFieldNumber;
+    case CONTACT_INFO:
+      return sync_pb::EntitySpecifics::kContactInfoFieldNumber;
+    case SAVED_TAB_GROUP:
+      return sync_pb::EntitySpecifics::kSavedTabGroupFieldNumber;
+    case POWER_BOOKMARK:
+      return sync_pb::EntitySpecifics::kPowerBookmarkFieldNumber;
+    case WEBAUTHN_CREDENTIAL:
+      return sync_pb::EntitySpecifics::kWebauthnCredentialFieldNumber;
+    case INCOMING_PASSWORD_SHARING_INVITATION:
+      return sync_pb::EntitySpecifics::
+          kIncomingPasswordSharingInvitationFieldNumber;
+    case OUTGOING_PASSWORD_SHARING_INVITATION:
+      return sync_pb::EntitySpecifics::
+          kOutgoingPasswordSharingInvitationFieldNumber;
+    case SHARED_TAB_GROUP_DATA:
+      return sync_pb::EntitySpecifics::kSharedTabGroupDataFieldNumber;
+    case COLLABORATION_GROUP:
+      return sync_pb::EntitySpecifics::kCollaborationGroupFieldNumber;
+    case PLUS_ADDRESS:
+      return sync_pb::EntitySpecifics::kPlusAddressFieldNumber;
+    case PRODUCT_COMPARISON:
+      return sync_pb::EntitySpecifics::kProductComparisonFieldNumber;
+    case COOKIES:
+      return sync_pb::EntitySpecifics::kCookieFieldNumber;
+    case PLUS_ADDRESS_SETTING:
+      return sync_pb::EntitySpecifics::kPlusAddressSettingFieldNumber;
+    case NIGORI:
+      return sync_pb::EntitySpecifics::kNigoriFieldNumber;
+  }
+  NOTREACHED();
 }
 
 void internal::GetDataTypeSetFromSpecificsFieldNumberListHelper(
@@ -682,15 +576,341 @@
 
 const char* DataTypeToDebugString(DataType data_type) {
   // This is used for displaying debug information.
-  return kDataTypeInfoMap[data_type].data_type_debug_string;
+  switch (data_type) {
+    case UNSPECIFIED:
+      return "Unspecified";
+    case BOOKMARKS:
+      return "Bookmarks";
+    case PREFERENCES:
+      return "Preferences";
+    case PASSWORDS:
+      return "Passwords";
+    case AUTOFILL_PROFILE:
+      return "Autofill Profiles";
+    case AUTOFILL:
+      return "Autofill";
+    case AUTOFILL_WALLET_CREDENTIAL:
+      return "Autofill Wallet Credential";
+    case AUTOFILL_WALLET_DATA:
+      return "Autofill Wallet";
+    case AUTOFILL_WALLET_METADATA:
+      return "Autofill Wallet Metadata";
+    case AUTOFILL_WALLET_OFFER:
+      return "Autofill Wallet Offer";
+    case AUTOFILL_WALLET_USAGE:
+      return "Autofill Wallet Usage";
+    case THEMES:
+      return "Themes";
+    case EXTENSIONS:
+      return "Extensions";
+    case SEARCH_ENGINES:
+      return "Search Engines";
+    case SESSIONS:
+      return "Sessions";
+    case APPS:
+      return "Apps";
+    case APP_SETTINGS:
+      return "App settings";
+    case EXTENSION_SETTINGS:
+      return "Extension settings";
+    case HISTORY_DELETE_DIRECTIVES:
+      return "History Delete Directives";
+    case DICTIONARY:
+      return "Dictionary";
+    case DEVICE_INFO:
+      return "Device Info";
+    case PRIORITY_PREFERENCES:
+      return "Priority Preferences";
+    case SUPERVISED_USER_SETTINGS:
+      return "Managed User Settings";
+    case APP_LIST:
+      return "App List";
+    case ARC_PACKAGE:
+      return "Arc Package";
+    case PRINTERS:
+      return "Printers";
+    case READING_LIST:
+      return "Reading List";
+    case USER_EVENTS:
+      return "User Events";
+    case USER_CONSENTS:
+      return "User Consents";
+    case SEND_TAB_TO_SELF:
+      return "Send Tab To Self";
+    case SECURITY_EVENTS:
+      return "Security Events";
+    case WIFI_CONFIGURATIONS:
+      return "Wifi Configurations";
+    case WEB_APPS:
+      return "Web Apps";
+    case WEB_APKS:
+      return "Web Apks";
+    case OS_PREFERENCES:
+      return "OS Preferences";
+    case OS_PRIORITY_PREFERENCES:
+      return "OS Priority Preferences";
+    case SHARING_MESSAGE:
+      return "Sharing Message";
+    case WORKSPACE_DESK:
+      return "Workspace Desk";
+    case HISTORY:
+      return "History";
+    case PRINTERS_AUTHORIZATION_SERVERS:
+      return "Printers Authorization Servers";
+    case CONTACT_INFO:
+      return "Contact Info";
+    case SAVED_TAB_GROUP:
+      return "Saved Tab Group";
+    case POWER_BOOKMARK:
+      return "Power Bookmark";
+    case WEBAUTHN_CREDENTIAL:
+      return "WebAuthn Credentials";
+    case INCOMING_PASSWORD_SHARING_INVITATION:
+      return "Incoming Password Sharing Invitations";
+    case OUTGOING_PASSWORD_SHARING_INVITATION:
+      return "Outgoing Password Sharing Invitations";
+    case SHARED_TAB_GROUP_DATA:
+      return "Shared Tab Group Data";
+    case COLLABORATION_GROUP:
+      return "Collaboration Group";
+    case PLUS_ADDRESS:
+      return "Plus Address";
+    case PRODUCT_COMPARISON:
+      return "Product Comparison";
+    case COOKIES:
+      return "Cookies";
+    case PLUS_ADDRESS_SETTING:
+      return "Plus Address Setting";
+    case NIGORI:
+      return "Encryption Keys";
+  }
+  NOTREACHED();
 }
 
 const char* DataTypeToHistogramSuffix(DataType data_type) {
-  return kDataTypeInfoMap[data_type].histogram_suffix;
+  // LINT.IfChange(DataTypeHistogramSuffix)
+  switch (data_type) {
+    case UNSPECIFIED:
+      return "";
+    case BOOKMARKS:
+      return "BOOKMARK";
+    case PREFERENCES:
+      return "PREFERENCE";
+    case PASSWORDS:
+      return "PASSWORD";
+    case AUTOFILL_PROFILE:
+      return "AUTOFILL_PROFILE";
+    case AUTOFILL:
+      return "AUTOFILL";
+    case AUTOFILL_WALLET_CREDENTIAL:
+      return "AUTOFILL_WALLET_CREDENTIAL";
+    case AUTOFILL_WALLET_DATA:
+      return "AUTOFILL_WALLET";
+    case AUTOFILL_WALLET_METADATA:
+      return "WALLET_METADATA";
+    case AUTOFILL_WALLET_OFFER:
+      return "AUTOFILL_OFFER";
+    case AUTOFILL_WALLET_USAGE:
+      return "AUTOFILL_WALLET_USAGE";
+    case THEMES:
+      return "THEME";
+    case EXTENSIONS:
+      return "EXTENSION";
+    case SEARCH_ENGINES:
+      return "SEARCH_ENGINE";
+    case SESSIONS:
+      return "SESSION";
+    case APPS:
+      return "APP";
+    case APP_SETTINGS:
+      return "APP_SETTING";
+    case EXTENSION_SETTINGS:
+      return "EXTENSION_SETTING";
+    case HISTORY_DELETE_DIRECTIVES:
+      return "HISTORY_DELETE_DIRECTIVE";
+    case DICTIONARY:
+      return "DICTIONARY";
+    case DEVICE_INFO:
+      return "DEVICE_INFO";
+    case PRIORITY_PREFERENCES:
+      return "PRIORITY_PREFERENCE";
+    case SUPERVISED_USER_SETTINGS:
+      return "MANAGED_USER_SETTING";
+    case APP_LIST:
+      return "APP_LIST";
+    case ARC_PACKAGE:
+      return "ARC_PACKAGE";
+    case PRINTERS:
+      return "PRINTER";
+    case READING_LIST:
+      return "READING_LIST";
+    case USER_EVENTS:
+      return "USER_EVENT";
+    case USER_CONSENTS:
+      return "USER_CONSENT";
+    case SEND_TAB_TO_SELF:
+      return "SEND_TAB_TO_SELF";
+    case SECURITY_EVENTS:
+      return "SECURITY_EVENT";
+    case WIFI_CONFIGURATIONS:
+      return "WIFI_CONFIGURATION";
+    case WEB_APPS:
+      return "WEB_APP";
+    case WEB_APKS:
+      return "WEB_APK";
+    case OS_PREFERENCES:
+      return "OS_PREFERENCE";
+    case OS_PRIORITY_PREFERENCES:
+      return "OS_PRIORITY_PREFERENCE";
+    case SHARING_MESSAGE:
+      return "SHARING_MESSAGE";
+    case WORKSPACE_DESK:
+      return "WORKSPACE_DESK";
+    case HISTORY:
+      return "HISTORY";
+    case PRINTERS_AUTHORIZATION_SERVERS:
+      return "PRINTERS_AUTHORIZATION_SERVER";
+    case CONTACT_INFO:
+      return "CONTACT_INFO";
+    case SAVED_TAB_GROUP:
+      return "SAVED_TAB_GROUP";
+    case POWER_BOOKMARK:
+      return "POWER_BOOKMARK";
+    case WEBAUTHN_CREDENTIAL:
+      return "WEBAUTHN_CREDENTIAL";
+    case INCOMING_PASSWORD_SHARING_INVITATION:
+      return "INCOMING_PASSWORD_SHARING_INVITATION";
+    case OUTGOING_PASSWORD_SHARING_INVITATION:
+      return "OUTGOING_PASSWORD_SHARING_INVITATION";
+    case SHARED_TAB_GROUP_DATA:
+      return "SHARED_TAB_GROUP_DATA";
+    case COLLABORATION_GROUP:
+      return "COLLABORATION_GROUP";
+    case PLUS_ADDRESS:
+      return "PLUS_ADDRESS";
+    case PRODUCT_COMPARISON:
+      return "PRODUCT_COMPARISON";
+    case COOKIES:
+      return "COOKIE";
+    case PLUS_ADDRESS_SETTING:
+      return "PLUS_ADDRESS_SETTING";
+    case NIGORI:
+      return "NIGORI";
+  }
+  // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:DataTypeHistogramSuffix)
+  NOTREACHED();
 }
 
 DataTypeForHistograms DataTypeHistogramValue(DataType data_type) {
-  return kDataTypeInfoMap[data_type].data_type_histogram_val;
+  switch (data_type) {
+    case UNSPECIFIED:
+      return DataTypeForHistograms::kUnspecified;
+    case BOOKMARKS:
+      return DataTypeForHistograms::kBookmarks;
+    case PREFERENCES:
+      return DataTypeForHistograms::kPreferences;
+    case PASSWORDS:
+      return DataTypeForHistograms::kPasswords;
+    case AUTOFILL_PROFILE:
+      return DataTypeForHistograms::kAutofillProfile;
+    case AUTOFILL:
+      return DataTypeForHistograms::kAutofill;
+    case AUTOFILL_WALLET_CREDENTIAL:
+      return DataTypeForHistograms::kAutofillWalletCredential;
+    case AUTOFILL_WALLET_DATA:
+      return DataTypeForHistograms::kAutofillWalletData;
+    case AUTOFILL_WALLET_METADATA:
+      return DataTypeForHistograms::kAutofillWalletMetadata;
+    case AUTOFILL_WALLET_OFFER:
+      return DataTypeForHistograms::kAutofillWalletOffer;
+    case AUTOFILL_WALLET_USAGE:
+      return DataTypeForHistograms::kAutofillWalletUsage;
+    case THEMES:
+      return DataTypeForHistograms::kThemes;
+    case EXTENSIONS:
+      return DataTypeForHistograms::kExtensions;
+    case SEARCH_ENGINES:
+      return DataTypeForHistograms::kSearchEngines;
+    case SESSIONS:
+      return DataTypeForHistograms::kSessions;
+    case APPS:
+      return DataTypeForHistograms::kApps;
+    case APP_SETTINGS:
+      return DataTypeForHistograms::kAppSettings;
+    case EXTENSION_SETTINGS:
+      return DataTypeForHistograms::kExtensionSettings;
+    case HISTORY_DELETE_DIRECTIVES:
+      return DataTypeForHistograms::kHistoryDeleteDirectices;
+    case DICTIONARY:
+      return DataTypeForHistograms::kDictionary;
+    case DEVICE_INFO:
+      return DataTypeForHistograms::kDeviceInfo;
+    case PRIORITY_PREFERENCES:
+      return DataTypeForHistograms::kPriorityPreferences;
+    case SUPERVISED_USER_SETTINGS:
+      return DataTypeForHistograms::kSupervisedUserSettings;
+    case APP_LIST:
+      return DataTypeForHistograms::kAppList;
+    case ARC_PACKAGE:
+      return DataTypeForHistograms::kArcPackage;
+    case PRINTERS:
+      return DataTypeForHistograms::kPrinters;
+    case READING_LIST:
+      return DataTypeForHistograms::kReadingList;
+    case USER_EVENTS:
+      return DataTypeForHistograms::kUserEvents;
+    case USER_CONSENTS:
+      return DataTypeForHistograms::kUserConsents;
+    case SEND_TAB_TO_SELF:
+      return DataTypeForHistograms::kSendTabToSelf;
+    case SECURITY_EVENTS:
+      return DataTypeForHistograms::kSecurityEvents;
+    case WIFI_CONFIGURATIONS:
+      return DataTypeForHistograms::kWifiConfigurations;
+    case WEB_APPS:
+      return DataTypeForHistograms::kWebApps;
+    case WEB_APKS:
+      return DataTypeForHistograms::kWebApks;
+    case OS_PREFERENCES:
+      return DataTypeForHistograms::kOsPreferences;
+    case OS_PRIORITY_PREFERENCES:
+      return DataTypeForHistograms::kOsPriorityPreferences;
+    case SHARING_MESSAGE:
+      return DataTypeForHistograms::kSharingMessage;
+    case WORKSPACE_DESK:
+      return DataTypeForHistograms::kWorkspaceDesk;
+    case HISTORY:
+      return DataTypeForHistograms::kHistory;
+    case PRINTERS_AUTHORIZATION_SERVERS:
+      return DataTypeForHistograms::kPrintersAuthorizationServers;
+    case CONTACT_INFO:
+      return DataTypeForHistograms::kContactInfo;
+    case SAVED_TAB_GROUP:
+      return DataTypeForHistograms::kSavedTabGroups;
+    case POWER_BOOKMARK:
+      return DataTypeForHistograms::kPowerBookmark;
+    case WEBAUTHN_CREDENTIAL:
+      return DataTypeForHistograms::kWebAuthnCredentials;
+    case INCOMING_PASSWORD_SHARING_INVITATION:
+      return DataTypeForHistograms::kIncomingPasswordSharingInvitations;
+    case OUTGOING_PASSWORD_SHARING_INVITATION:
+      return DataTypeForHistograms::kOutgoingPasswordSharingInvitations;
+    case SHARED_TAB_GROUP_DATA:
+      return DataTypeForHistograms::kSharedTabGroupData;
+    case COLLABORATION_GROUP:
+      return DataTypeForHistograms::kCollaborationGroup;
+    case PLUS_ADDRESS:
+      return DataTypeForHistograms::kPlusAddresses;
+    case PRODUCT_COMPARISON:
+      return DataTypeForHistograms::kProductComparison;
+    case COOKIES:
+      return DataTypeForHistograms::kCookies;
+    case PLUS_ADDRESS_SETTING:
+      return DataTypeForHistograms::kPlusAddressSettings;
+    case NIGORI:
+      return DataTypeForHistograms::kNigori;
+  }
+  NOTREACHED();
 }
 
 int DataTypeToStableIdentifier(DataType data_type) {
@@ -709,6 +929,122 @@
   return result;
 }
 
+const char* DataTypeToStableLowerCaseString(DataType data_type) {
+  // WARNING: existing strings must not be changed without migration, they are
+  // persisted!
+  switch (data_type) {
+    case UNSPECIFIED:
+      return "";
+    case BOOKMARKS:
+      return "bookmarks";
+    case PREFERENCES:
+      return "preferences";
+    case PASSWORDS:
+      return "passwords";
+    case AUTOFILL_PROFILE:
+      return "autofill_profiles";
+    case AUTOFILL:
+      return "autofill";
+    case AUTOFILL_WALLET_CREDENTIAL:
+      return "autofill_wallet_credential";
+    case AUTOFILL_WALLET_DATA:
+      return "autofill_wallet";
+    case AUTOFILL_WALLET_METADATA:
+      return "autofill_wallet_metadata";
+    case AUTOFILL_WALLET_OFFER:
+      return "autofill_wallet_offer";
+    case AUTOFILL_WALLET_USAGE:
+      return "autofill_wallet_usage";
+    case THEMES:
+      return "themes";
+    case EXTENSIONS:
+      return "extensions";
+    case SEARCH_ENGINES:
+      return "search_engines";
+    case SESSIONS:
+      return "sessions";
+    case APPS:
+      return "apps";
+    case APP_SETTINGS:
+      return "app_settings";
+    case EXTENSION_SETTINGS:
+      return "extension_settings";
+    case HISTORY_DELETE_DIRECTIVES:
+      return "history_delete_directives";
+    case DICTIONARY:
+      return "dictionary";
+    case DEVICE_INFO:
+      return "device_info";
+    case PRIORITY_PREFERENCES:
+      return "priority_preferences";
+    case SUPERVISED_USER_SETTINGS:
+      return "managed_user_settings";
+    case APP_LIST:
+      return "app_list";
+    case ARC_PACKAGE:
+      return "arc_package";
+    case PRINTERS:
+      return "printers";
+    case READING_LIST:
+      return "reading_list";
+    case USER_EVENTS:
+      return "user_events";
+    case USER_CONSENTS:
+      return "user_consent";
+    case SEND_TAB_TO_SELF:
+      return "send_tab_to_self";
+    case SECURITY_EVENTS:
+      return "security_events";
+    case WIFI_CONFIGURATIONS:
+      return "wifi_configurations";
+    case WEB_APPS:
+      return "web_apps";
+    case WEB_APKS:
+      return "web_apks";
+    case OS_PREFERENCES:
+      return "os_preferences";
+    case OS_PRIORITY_PREFERENCES:
+      return "os_priority_preferences";
+    case SHARING_MESSAGE:
+      return "sharing_message";
+    case WORKSPACE_DESK:
+      return "workspace_desk";
+    case HISTORY:
+      return "history";
+    case PRINTERS_AUTHORIZATION_SERVERS:
+      return "printers_authorization_servers";
+    case CONTACT_INFO:
+      return "contact_info";
+    case SAVED_TAB_GROUP:
+      return "saved_tab_group";
+    case POWER_BOOKMARK:
+      return "power_bookmark";
+    case WEBAUTHN_CREDENTIAL:
+      return "webauthn_credential";
+    case INCOMING_PASSWORD_SHARING_INVITATION:
+      return "incoming_password_sharing_invitation";
+    case OUTGOING_PASSWORD_SHARING_INVITATION:
+      return "outgoing_password_sharing_invitation";
+    case SHARED_TAB_GROUP_DATA:
+      return "shared_tab_group_data";
+    case COLLABORATION_GROUP:
+      return "collaboration_group";
+    case PLUS_ADDRESS:
+      return "plus_address";
+    case PRODUCT_COMPARISON:
+      return "product_comparison";
+    case COOKIES:
+      return "cookies";
+    case PLUS_ADDRESS_SETTING:
+      return "plus_address_setting";
+    case NIGORI:
+      return "nigori";
+  }
+  // WARNING: existing strings must not be changed without migration, they
+  // are persisted!
+  NOTREACHED();
+}
+
 std::ostream& operator<<(std::ostream& out, DataTypeSet data_type_set) {
   return out << DataTypeSetToDebugString(data_type_set);
 }
@@ -716,16 +1052,11 @@
 std::string DataTypeToProtocolRootTag(DataType data_type) {
   DCHECK(ProtocolTypes().Has(data_type));
   DCHECK(IsRealDataType(data_type));
-  const std::string root_tag =
-      std::string(kDataTypeInfoMap[data_type].lowercase_root_tag);
+  const std::string root_tag = DataTypeToStableLowerCaseString(data_type);
   DCHECK(!root_tag.empty());
   return "google_chrome_" + root_tag;
 }
 
-const char* GetDataTypeLowerCaseRootTag(DataType data_type) {
-  return kDataTypeInfoMap[data_type].lowercase_root_tag;
-}
-
 bool IsRealDataType(DataType data_type) {
   return data_type >= FIRST_REAL_DATA_TYPE &&
          data_type <= LAST_REAL_DATA_TYPE;
diff --git a/components/sync/base/data_type.h b/components/sync/base/data_type.h
index c434cdbe..c6477dd 100644
--- a/components/sync/base/data_type.h
+++ b/components/sync/base/data_type.h
@@ -25,11 +25,6 @@
 //
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.sync
-//
-// |kDataTypeInfoMap| struct entries are in the same order as their definition
-// in DataType enum. When you make changes in DataType enum, don't forget to
-// update the |kDataTypeInfoMap| struct in data_type.cc and also the
-// SyncDataType histogram suffix in histograms.xml
 enum DataType {
   // Object type unknown. This may be used when:
   // a) The client received *valid* data from a data type which this version
@@ -462,6 +457,10 @@
 // time and thus can be used when persisting data.
 int DataTypeToStableIdentifier(DataType data_type);
 
+// This returns a string that is stable over time and thus can be used for local
+// persistence. It is guaranteed to be lowercase.
+const char* DataTypeToStableLowerCaseString(DataType data_type);
+
 // Returns the comma-separated string representation of |data_types|.
 std::string DataTypeSetToDebugString(DataTypeSet data_types);
 
@@ -476,11 +475,6 @@
 // not return the root entity.
 std::string DataTypeToProtocolRootTag(DataType data_type);
 
-// As opposed to DataTypeToProtocolRootTag(), this returns a string that isn't
-// exposed in the sync protocol, but that is still stable and thus can be used
-// for local persistence. It is guaranteed to be lowercase.
-const char* GetDataTypeLowerCaseRootTag(DataType data_type);
-
 // Returns true if |data_type| is a real datatype
 bool IsRealDataType(DataType data_type);
 
diff --git a/components/sync/base/pref_names.h b/components/sync/base/pref_names.h
index dd0f8f3..c5c0d3575 100644
--- a/components/sync/base/pref_names.h
+++ b/components/sync/base/pref_names.h
@@ -125,7 +125,7 @@
 inline constexpr char kSyncFeatureStatusForSyncToSigninMigration[] =
     "sync.feature_status_for_sync_to_signin";
 // Prefix for boolean per-data-type statuses, to be suffixed with "." plus
-// GetDataTypeLowerCaseRootTag().
+// DataTypeToStableLowerCaseString().
 inline constexpr char kSyncDataTypeStatusForSyncToSigninMigrationPrefix[] =
     "sync.data_type_status_for_sync_to_signin";
 
diff --git a/components/sync/model/blocking_data_type_store_impl.cc b/components/sync/model/blocking_data_type_store_impl.cc
index 8346e93..4bd24fb12 100644
--- a/components/sync/model/blocking_data_type_store_impl.cc
+++ b/components/sync/model/blocking_data_type_store_impl.cc
@@ -285,7 +285,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return backend_->DeleteDataAndMetadataForPrefix(
       base::StrCat({GetStorageTypePrefix(storage_type_),
-                    GetDataTypeLowerCaseRootTag(data_type_)}));
+                    DataTypeToStableLowerCaseString(data_type_)}));
 }
 
 // static
@@ -300,7 +300,7 @@
     DataType data_type,
     StorageType storage_type) {
   return base::StrCat({GetStorageTypePrefix(storage_type),
-                       GetDataTypeLowerCaseRootTag(data_type)});
+                       DataTypeToStableLowerCaseString(data_type)});
 }
 
 }  // namespace syncer
diff --git a/components/sync/service/sync_feature_status_for_migrations_recorder.cc b/components/sync/service/sync_feature_status_for_migrations_recorder.cc
index 6a48696..e9a07554 100644
--- a/components/sync/service/sync_feature_status_for_migrations_recorder.cc
+++ b/components/sync/service/sync_feature_status_for_migrations_recorder.cc
@@ -119,7 +119,7 @@
     DataType type) {
   return base::StrCat(
       {prefs::internal::kSyncDataTypeStatusForSyncToSigninMigrationPrefix, ".",
-       GetDataTypeLowerCaseRootTag(type)});
+       DataTypeToStableLowerCaseString(type)});
 }
 
 SyncFeatureStatusForSyncToSigninMigration
diff --git a/components/sync/service/sync_feature_status_for_migrations_recorder_unittest.cc b/components/sync/service/sync_feature_status_for_migrations_recorder_unittest.cc
index 525937f6..6e8b2edc 100644
--- a/components/sync/service/sync_feature_status_for_migrations_recorder_unittest.cc
+++ b/components/sync/service/sync_feature_status_for_migrations_recorder_unittest.cc
@@ -49,7 +49,7 @@
   bool GetDataTypeStatus(DataType type) const {
     return pref_service_.GetBoolean(base::StrCat(
         {prefs::internal::kSyncDataTypeStatusForSyncToSigninMigrationPrefix,
-         ".", GetDataTypeLowerCaseRootTag(type)}));
+         ".", DataTypeToStableLowerCaseString(type)}));
   }
 
   TestSyncService& sync_service() { return sync_service_; }
diff --git a/components/user_manager/user_manager.cc b/components/user_manager/user_manager.cc
index a3c3e3d..d0686b7 100644
--- a/components/user_manager/user_manager.cc
+++ b/components/user_manager/user_manager.cc
@@ -66,6 +66,19 @@
 
 UserManager::UserAccountData::~UserAccountData() {}
 
+UserManager::DeviceLocalAccountInfo::DeviceLocalAccountInfo(std::string user_id,
+                                                            UserType type)
+    : user_id(std::move(user_id)), type(type) {}
+
+UserManager::DeviceLocalAccountInfo::DeviceLocalAccountInfo(
+    const UserManager::DeviceLocalAccountInfo&) = default;
+
+UserManager::DeviceLocalAccountInfo&
+UserManager::DeviceLocalAccountInfo::operator=(
+    const UserManager::DeviceLocalAccountInfo&) = default;
+
+UserManager::DeviceLocalAccountInfo::~DeviceLocalAccountInfo() = default;
+
 void UserManager::Initialize() {
   DCHECK(!UserManager::instance);
   UserManager::SetInstance(this);
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index 38de28e..b0d5e27 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -33,6 +33,7 @@
   DEVICE_EPHEMERAL_USERS_ENABLED = 4,
   GAIA_REMOVED = 5,
   MISCONFIGURED_USER = 6,
+  DEVICE_LOCAL_ACCOUNT_UPDATED = 7,
 };
 
 // Interface for UserManagerBase - that provides base implementation for
@@ -143,6 +144,23 @@
     const std::string locale_;
   };
 
+  // Info to build a device local account.
+  struct DeviceLocalAccountInfo {
+    DeviceLocalAccountInfo(std::string user_id, UserType type);
+    DeviceLocalAccountInfo(const DeviceLocalAccountInfo&);
+    DeviceLocalAccountInfo& operator=(const DeviceLocalAccountInfo&);
+    ~DeviceLocalAccountInfo();
+
+    // Corresponding to AccountId's user email.
+    std::string user_id;
+
+    // Type of the device local account.
+    UserType type;
+
+    // Display name. Can be set only if the type is kPublicAccount.
+    std::optional<std::u16string> display_name;
+  };
+
   // Initializes UserManager instance to this. Normally should be called right
   // after creation so that user_manager::UserManager::Get() doesn't fail.
   // Tests could call this method if they are replacing existing UserManager
diff --git a/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.cc b/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.cc
index 72217d73..060dfa3 100644
--- a/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.cc
+++ b/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.cc
@@ -34,9 +34,16 @@
 
 EcdsaP256SHA256Signature::EcdsaP256SHA256Signature(
     const EcdsaP256SHA256Signature&) = default;
+
+EcdsaP256SHA256Signature::EcdsaP256SHA256Signature(EcdsaP256SHA256Signature&&) =
+    default;
+
 EcdsaP256SHA256Signature& EcdsaP256SHA256Signature::operator=(
     const EcdsaP256SHA256Signature&) = default;
 
+EcdsaP256SHA256Signature& EcdsaP256SHA256Signature::operator=(
+    EcdsaP256SHA256Signature&&) = default;
+
 EcdsaP256SHA256Signature::EcdsaP256SHA256Signature(
     mojo::DefaultConstruct::Tag) {}
 
diff --git a/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.h b/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.h
index 85539b95..bfc5baa3f 100644
--- a/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.h
+++ b/components/web_package/signed_web_bundles/ecdsa_p256_sha256_signature.h
@@ -25,10 +25,14 @@
   static base::expected<EcdsaP256SHA256Signature, std::string> Create(
       base::span<const uint8_t> bytes);
 
-  ~EcdsaP256SHA256Signature();
+  explicit EcdsaP256SHA256Signature(mojo::DefaultConstruct::Tag);
 
   EcdsaP256SHA256Signature(const EcdsaP256SHA256Signature&);
+  EcdsaP256SHA256Signature(EcdsaP256SHA256Signature&&);
   EcdsaP256SHA256Signature& operator=(const EcdsaP256SHA256Signature&);
+  EcdsaP256SHA256Signature& operator=(EcdsaP256SHA256Signature&&);
+
+  ~EcdsaP256SHA256Signature();
 
   bool operator==(const EcdsaP256SHA256Signature& other) const = default;
 
@@ -37,7 +41,6 @@
 
   base::span<const uint8_t> bytes() const { return *bytes_; }
 
-  explicit EcdsaP256SHA256Signature(mojo::DefaultConstruct::Tag);
 
  private:
   explicit EcdsaP256SHA256Signature(std::vector<uint8_t> bytes);
diff --git a/components/web_package/signed_web_bundles/ed25519_signature.h b/components/web_package/signed_web_bundles/ed25519_signature.h
index d9dc3fd..92f3cad 100644
--- a/components/web_package/signed_web_bundles/ed25519_signature.h
+++ b/components/web_package/signed_web_bundles/ed25519_signature.h
@@ -28,6 +28,12 @@
 
   static Ed25519Signature Create(base::span<const uint8_t, kLength> bytes);
 
+  explicit Ed25519Signature(mojo::DefaultConstruct::Tag) {}
+  Ed25519Signature(const Ed25519Signature&) = default;
+  Ed25519Signature(Ed25519Signature&&) = default;
+  Ed25519Signature& operator=(const Ed25519Signature&) = default;
+  Ed25519Signature& operator=(Ed25519Signature&&) = default;
+
   bool operator==(const Ed25519Signature& other) const;
   bool operator!=(const Ed25519Signature& other) const;
 
@@ -36,8 +42,6 @@
 
   const std::array<uint8_t, kLength>& bytes() const { return *bytes_; }
 
-  explicit Ed25519Signature(mojo::DefaultConstruct::Tag) {}
-
  private:
   FRIEND_TEST_ALL_PREFIXES(StructTraitsTest, Ed25519Signature);
 
diff --git a/components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.cc b/components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.cc
index 5a54e0cd..745d15d 100644
--- a/components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.cc
+++ b/components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.cc
@@ -26,10 +26,17 @@
 SignedWebBundleSignatureStackEntry::SignedWebBundleSignatureStackEntry(
     const SignedWebBundleSignatureStackEntry&) = default;
 
+SignedWebBundleSignatureStackEntry::SignedWebBundleSignatureStackEntry(
+    SignedWebBundleSignatureStackEntry&&) = default;
+
 SignedWebBundleSignatureStackEntry&
 SignedWebBundleSignatureStackEntry::operator=(
     const SignedWebBundleSignatureStackEntry&) = default;
 
+SignedWebBundleSignatureStackEntry&
+SignedWebBundleSignatureStackEntry::operator=(
+    SignedWebBundleSignatureStackEntry&&) = default;
+
 SignedWebBundleSignatureStackEntry::~SignedWebBundleSignatureStackEntry() =
     default;
 
diff --git a/components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.h b/components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.h
index 7ee982c..cd6c1d72 100644
--- a/components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.h
+++ b/components/web_package/signed_web_bundles/signed_web_bundle_signature_stack_entry.h
@@ -22,8 +22,12 @@
 
   SignedWebBundleSignatureInfoBase(const SignedWebBundleSignatureInfoBase&) =
       default;
+  SignedWebBundleSignatureInfoBase(SignedWebBundleSignatureInfoBase&&) =
+      default;
   SignedWebBundleSignatureInfoBase& operator=(
       const SignedWebBundleSignatureInfoBase&) = default;
+  SignedWebBundleSignatureInfoBase& operator=(
+      SignedWebBundleSignatureInfoBase&&) = default;
 
   ~SignedWebBundleSignatureInfoBase() = default;
 
@@ -46,8 +50,12 @@
 
   SignedWebBundleSignatureInfoUnknown(
       const SignedWebBundleSignatureInfoUnknown&) = default;
+  SignedWebBundleSignatureInfoUnknown(SignedWebBundleSignatureInfoUnknown&&) =
+      default;
   SignedWebBundleSignatureInfoUnknown& operator=(
       const SignedWebBundleSignatureInfoUnknown&) = default;
+  SignedWebBundleSignatureInfoUnknown& operator=(
+      SignedWebBundleSignatureInfoUnknown&&) = default;
 
   ~SignedWebBundleSignatureInfoUnknown() = default;
 
@@ -77,8 +85,11 @@
       SignedWebBundleSignatureInfo signature_info);
 
   SignedWebBundleSignatureStackEntry(const SignedWebBundleSignatureStackEntry&);
+  SignedWebBundleSignatureStackEntry(SignedWebBundleSignatureStackEntry&&);
   SignedWebBundleSignatureStackEntry& operator=(
       const SignedWebBundleSignatureStackEntry&);
+  SignedWebBundleSignatureStackEntry& operator=(
+      SignedWebBundleSignatureStackEntry&&);
 
   ~SignedWebBundleSignatureStackEntry();
 
diff --git a/components/webdata/common/web_database_service.cc b/components/webdata/common/web_database_service.cc
index 9e8e566..6a90d9f9 100644
--- a/components/webdata/common/web_database_service.cc
+++ b/components/webdata/common/web_database_service.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/check.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/task/sequenced_task_runner.h"
@@ -19,6 +20,20 @@
 #include "components/webdata/common/web_data_service_consumer.h"
 #include "components/webdata/common/web_database_backend.h"
 
+namespace features {
+
+// If enabled, then an Encryptor will be requested that is not always backwards
+// compatible with OSCrypt Sync. On some platforms, this might mean a key is
+// used that is stored more securely, such as using App-Bound encryption on
+// Windows.
+// If this feature is enabled, any data stored by `WebDatabaseService` is not
+// guaranteed to be retrievable if OSCrypt Async is not used.
+BASE_FEATURE(kUseNewEncryptionKeyForWebData,
+             "UseNewEncryptionKeyForWebData",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+}  // namespace features
+
 // Receives messages from the backend on the DB sequence, posts them to
 // WebDatabaseService on the UI sequence.
 class WebDatabaseService::BackendDelegate
@@ -81,11 +96,14 @@
 }
 
 void WebDatabaseService::LoadDatabase(os_crypt_async::OSCryptAsync* os_crypt) {
+  const auto option =
+      base::FeatureList::IsEnabled(features::kUseNewEncryptionKeyForWebData)
+          ? os_crypt_async::Encryptor::Option::kNone
+          : os_crypt_async::Encryptor::Option::kEncryptSyncCompat;
   // TODO(crbug.com/40267945): Place kEncryptSyncCompat behind base::Feature and
   // then remove it.
   subscription_ = os_crypt->GetInstance(
-      base::BindOnce(&WebDatabaseService::CompleteLoadDatabase, this),
-      os_crypt_async::Encryptor::Option::kEncryptSyncCompat);
+      base::BindOnce(&WebDatabaseService::CompleteLoadDatabase, this), option);
 }
 
 void WebDatabaseService::ShutdownDatabase() {
diff --git a/components/webdata/common/web_database_service.h b/components/webdata/common/web_database_service.h
index 4533c655..c98b1b1 100644
--- a/components/webdata/common/web_database_service.h
+++ b/components/webdata/common/web_database_service.h
@@ -13,6 +13,7 @@
 
 #include "base/callback_list.h"
 #include "base/compiler_specific.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/ref_counted.h"
@@ -38,6 +39,9 @@
 class WDTypedResult;
 class WebDataServiceConsumer;
 
+namespace features {
+WEBDATA_EXPORT BASE_DECLARE_FEATURE(kUseNewEncryptionKeyForWebData);
+}  // namespace features
 
 ////////////////////////////////////////////////////////////////////////////////
 //
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 8cae82e..ea1549e8 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1858,6 +1858,8 @@
     "renderer_host/page_impl.h",
     "renderer_host/page_lifecycle_state_manager.cc",
     "renderer_host/page_lifecycle_state_manager.h",
+    "renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.cc",
+    "renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.h",
     "renderer_host/policy_container_host.cc",
     "renderer_host/policy_container_host.h",
     "renderer_host/private_network_access_util.cc",
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
index d62b0097..f4fc397 100644
--- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -1413,45 +1413,6 @@
 #endif
 }
 
-TEST_F(BrowserAccessibilityManagerTest,
-       TestShouldFireEventForAlertEventWithEmptyName) {
-  ui::TestAXTreeUpdate update(std::string(R"HTML(
-    ++1 kRootWebArea
-    ++++11 kParagraph
-    ++++++111 kAlert
-    ++++++++1111 kStaticText
-  )HTML"));
-
-  std::unique_ptr<ui::BrowserAccessibilityManager> manager(
-      CreateBrowserAccessibilityManager(
-          update, node_id_delegate_,
-          test_browser_accessibility_delegate_.get()));
-
-  EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(1)));
-  EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(11)));
-  EXPECT_FALSE(manager->ShouldFireEventForNode(manager->GetFromID(111)));
-}
-
-TEST_F(BrowserAccessibilityManagerTest,
-       TestShouldFireEventForAlertEventWithNonEmptyName) {
-  ui::TestAXTreeUpdate update(std::string(R"HTML(
-    ++1 kRootWebArea
-    ++++11 kParagraph
-    ++++++111 kAlert
-    ++++++++1111 kStaticText
-  )HTML"));
-
-  update.nodes[3].SetName("Test alert message.");
-  std::unique_ptr<ui::BrowserAccessibilityManager> manager(
-      CreateBrowserAccessibilityManager(
-          update, node_id_delegate_,
-          test_browser_accessibility_delegate_.get()));
-
-  EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(1)));
-  EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(11)));
-  EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(111)));
-}
-
 TEST_F(BrowserAccessibilityManagerTest, NestedChildRoot) {
   ui::AXNodeData root;
   root.id = 1;
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index 4e692b6..38c96d4b8 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -463,11 +463,6 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
-                       AccessibilityEventsAddAlertContentEmptyText) {
-  RunEventTest(FILE_PATH_LITERAL("add-alert-with-empty-text.html"));
-}
-
-IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
                        AccessibilityEventsAddChild) {
   RunEventTest(FILE_PATH_LITERAL("add-child.html"));
 }
diff --git a/content/browser/attribution_reporting/attribution_features.cc b/content/browser/attribution_reporting/attribution_features.cc
index b3256ab..ffe4dbf3 100644
--- a/content/browser/attribution_reporting/attribution_features.cc
+++ b/content/browser/attribution_reporting/attribution_features.cc
@@ -4,21 +4,7 @@
 
 #include "content/browser/attribution_reporting/attribution_features.h"
 
-#include "base/feature_list.h"
-#include "base/time/time.h"
-
 namespace content {
-
-BASE_FEATURE(kAttributionReportDeliveryRetryDelays,
-             "AttributionReportDeliveryRetryDelays",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-const base::FeatureParam<base::TimeDelta>
-    kAttributionReportDeliveryFirstRetryDelay{
-        &kAttributionReportDeliveryRetryDelays, "first_retry_delay",
-        base::Minutes(5)};
-const base::FeatureParam<base::TimeDelta>
-    kAttributionReportDeliverySecondRetryDelay{
-        &kAttributionReportDeliveryRetryDelays, "second_retry_delay",
-        base::Minutes(15)};
-
+// TODO(crbug.com/365974433): Add feature flag to gate third report delay
+// window.
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_features.h b/content/browser/attribution_reporting/attribution_features.h
index 3f85280..b4b0e613 100644
--- a/content/browser/attribution_reporting/attribution_features.h
+++ b/content/browser/attribution_reporting/attribution_features.h
@@ -5,22 +5,9 @@
 #ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_FEATURES_H_
 #define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_FEATURES_H_
 
-#include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
-#include "content/common/content_export.h"
-
-namespace base {
-class TimeDelta;
-}  // namespace base
-
 namespace content {
-
-CONTENT_EXPORT BASE_DECLARE_FEATURE(kAttributionReportDeliveryRetryDelays);
-CONTENT_EXPORT extern const base::FeatureParam<base::TimeDelta>
-    kAttributionReportDeliveryFirstRetryDelay;
-CONTENT_EXPORT extern const base::FeatureParam<base::TimeDelta>
-    kAttributionReportDeliverySecondRetryDelay;
-
+// TODO(crbug.com/365974433): Add feature flag to gate third report delay
+// window.
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_FEATURES_H_
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.cc b/content/browser/attribution_reporting/attribution_manager_impl.cc
index a0ad18f..e9a10fd 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl.cc
@@ -140,6 +140,9 @@
 
 const base::TimeDelta kPrivacySandboxAttestationsTimeout = base::Minutes(5);
 
+const base::TimeDelta kReportDeliveryFirstRetryDelay = base::Minutes(5);
+const base::TimeDelta kReportDeliverySecondRetryDelay = base::Minutes(15);
+
 }  // namespace
 
 // This class consolidates logic regarding when to schedule the browser to send
@@ -231,8 +234,8 @@
 }
 
 const base::TimeDelta ReportRetryDelay(bool is_first_retry) {
-  return is_first_retry ? kAttributionReportDeliveryFirstRetryDelay.Get()
-                        : kAttributionReportDeliverySecondRetryDelay.Get();
+  return is_first_retry ? kReportDeliveryFirstRetryDelay
+                        : kReportDeliverySecondRetryDelay;
 }
 
 void RecordStoreSourceStatus(const StoreSourceResult& result) {
diff --git a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
index 4816eff..7ac9f1b 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
@@ -2059,66 +2059,6 @@
       "Conversions.EventLevelReport.ReportRetriesTillSuccessOrFailure", 1, 1);
 }
 
-TEST_F(AttributionManagerImplTest, ReportRetryDelayFeatureParams) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeaturesAndParameters(
-      {{kAttributionReportDeliveryRetryDelays,
-        {
-            {"first_retry_delay", "1m"},
-            {"second_retry_delay", "2m"},
-        }}},
-      {});
-  base::HistogramTester histograms;
-
-  bool was_report_sent = false;
-
-  Checkpoint checkpoint;
-  {
-    InSequence seq;
-
-    EXPECT_CALL(*report_sender_, SendReport(_, /*is_debug_report=*/false, _))
-        .WillOnce(InvokeReportSentCallback(SentResult::kTransientFailure));
-
-    EXPECT_CALL(checkpoint, Call(1));
-    EXPECT_CALL(*report_sender_, SendReport(_, /*is_debug_report=*/false, _))
-        .WillOnce(InvokeReportSentCallback(SentResult::kTransientFailure));
-
-    EXPECT_CALL(checkpoint, Call(2));
-    EXPECT_CALL(*report_sender_, SendReport(_, /*is_debug_report=*/false, _))
-        .WillOnce([&](AttributionReport report, bool is_debug_report,
-                      ReportSentCallback callback) {
-          std::move(callback).Run(std::move(report),
-                                  SendResult::Sent(SentResult::kSent,
-                                                   /*status=*/0));
-          was_report_sent = true;
-        });
-  }
-
-  attribution_manager_->HandleSource(
-      SourceBuilder().SetExpiry(kImpressionExpiry).Build(), kFrameId);
-  attribution_manager_->HandleTrigger(DefaultTrigger(), kFrameId);
-
-  task_environment_.FastForwardBy(kFirstReportingWindow);
-
-  checkpoint.Call(1);
-
-  // First report delay.
-  task_environment_.FastForwardBy(base::Minutes(1));
-
-  checkpoint.Call(2);
-
-  // Second report delay.
-  task_environment_.FastForwardBy(base::Minutes(2));
-
-  ASSERT_TRUE(was_report_sent);
-
-  // kSuccess = 0.
-  histograms.ExpectUniqueSample("Conversions.ReportSendOutcome3", 0, 1);
-
-  histograms.ExpectUniqueSample(
-      "Conversions.EventLevelReport.ReportRetriesTillSuccessOrFailure", 2, 1);
-}
-
 TEST_F(AttributionManagerImplTest, SendReport_RecordsExtraReportDelay2) {
   base::HistogramTester histograms;
 
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index dc9e4513..51eac2e 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -237,10 +237,6 @@
   return file_size;
 }
 
-void DeduplicateSourceIds(std::vector<StoredSource::Id>& ids) {
-  ids = base::flat_set<StoredSource::Id>(std::move(ids)).extract();
-}
-
 }  // namespace
 
 struct AttributionStorageSql::ReportCorruptionStatusSetAndIds {
diff --git a/content/browser/attribution_reporting/rate_limit_table.cc b/content/browser/attribution_reporting/rate_limit_table.cc
index 4445dbf..715e575 100644
--- a/content/browser/attribution_reporting/rate_limit_table.cc
+++ b/content/browser/attribution_reporting/rate_limit_table.cc
@@ -6,8 +6,11 @@
 
 #include <stdint.h>
 
+#include <limits>
+#include <map>
 #include <set>
 #include <string>
+#include <tuple>
 #include <vector>
 
 #include "base/check.h"
@@ -16,6 +19,7 @@
 #include "base/containers/span.h"
 #include "base/memory/raw_ref.h"
 #include "base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "base/ranges/functional.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
@@ -53,29 +57,6 @@
   NOTREACHED();
 }
 
-struct DestinationLimitRecord {
-  std::string serialized_destination;
-  base::Time time;
-  int64_t priority;
-  StoredSource::Id source_id;
-
-  bool operator>(const DestinationLimitRecord& other) const {
-    if (priority > other.priority) {
-      return true;
-    }
-    if (priority < other.priority) {
-      return false;
-    }
-    if (time > other.time) {
-      return true;
-    }
-    if (time < other.time) {
-      return false;
-    }
-    return serialized_destination > other.serialized_destination;
-  }
-};
-
 }  // namespace
 
 RateLimitTable::RateLimitTable(const AttributionResolverDelegate* delegate)
@@ -368,6 +349,103 @@
   return RateLimitResult::kAllowed;
 }
 
+namespace {
+
+struct DestinationAttribute {
+  int64_t priority = std::numeric_limits<int64_t>::min();
+  base::Time time = base::Time::Min();
+
+  bool operator<(const DestinationAttribute& other) const {
+    return std::tie(priority, time) < std::tie(other.priority, other.time);
+  }
+};
+
+struct DestinationData {
+  DestinationAttribute attribute;
+  std::vector<StoredSource::Id> sources;
+
+  DestinationData() = default;
+
+  DestinationData(const DestinationData&) = delete;
+  DestinationData& operator=(const DestinationData&) = delete;
+
+  DestinationData(DestinationData&&) = default;
+  DestinationData& operator=(DestinationData&&) = default;
+
+  void Assign(std::vector<StoredSource::Id>& source_ids) && {
+    if (source_ids.empty()) {
+      source_ids = std::move(sources);
+    } else {
+      source_ids.insert(source_ids.end(), sources.begin(), sources.end());
+    }
+  }
+};
+
+using DestinationDataMap = std::map<std::string, DestinationData>;
+
+void AddDestination(DestinationDataMap& destination_datas,
+                    std::string destination,
+                    StoredSource::Id source_id,
+                    DestinationAttribute attribute) {
+  auto [destination_data, _] =
+      destination_datas.try_emplace(std::move(destination), DestinationData());
+  destination_data->second.attribute =
+      std::max(attribute, destination_data->second.attribute);
+  destination_data->second.sources.push_back(source_id);
+}
+
+// Returns source IDs of the unselected destinations.
+std::vector<StoredSource::Id> SelectDestinations(
+    DestinationDataMap destination_datas,
+    size_t destinations_allowed) {
+  if (destination_datas.size() <= destinations_allowed) {
+    return {};
+  }
+
+  // Currently the limit on production is 100 and the maximum size of
+  // `destination_datas` is 100 + 3 (max destinations per source) = 103,
+  // therefore it's more efficient to find the bottom destinations than the top
+  // and delete the selected destinations.
+  size_t to_select = destination_datas.size() - destinations_allowed;
+
+  const auto cmp = [](const DestinationDataMap::node_type& a,
+                      const DestinationDataMap::node_type& b) {
+    return std::tie(a.mapped().attribute, a.key()) <
+           std::tie(b.mapped().attribute, b.key());
+  };
+
+  std::vector<DestinationDataMap::node_type> selected;
+  selected.reserve(to_select);
+
+  while (!destination_datas.empty() && selected.size() < to_select) {
+    selected.emplace_back(destination_datas.extract(destination_datas.begin()));
+  }
+
+  base::ranges::make_heap(selected, cmp);
+
+  while (!destination_datas.empty()) {
+    auto destination = destination_datas.extract(destination_datas.begin());
+
+    if (cmp(destination, selected.front())) {
+      base::ranges::pop_heap(selected, cmp);
+      std::swap(selected.back(), destination);
+      base::ranges::push_heap(selected, cmp);
+    }
+  }
+
+  std::vector<StoredSource::Id> source_ids;
+
+  for (auto& destination : selected) {
+    std::move(destination.mapped()).Assign(source_ids);
+  }
+
+  DeduplicateSourceIds(source_ids);
+
+  return source_ids;
+}
+
+}  // namespace
+
 base::expected<std::vector<StoredSource::Id>, RateLimitTable::Error>
 RateLimitTable::GetSourcesToDeactivateForDestinationLimit(
     sql::Database* db,
@@ -375,6 +453,17 @@
     base::Time source_time) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  DestinationDataMap destination_datas;
+
+  for (const auto& destination :
+       source.registration().destination_set.destinations()) {
+    AddDestination(
+        destination_datas, destination.Serialize(),
+        StoredSource::Id(kUnsetRecordId),
+        DestinationAttribute(source.registration().destination_limit_priority,
+                             source_time));
+  }
+
   // Check the number of unique destinations covered by all source registrations
   // whose [source_time, source_expiry_or_attribution_time] intersect with the
   // current source_time.
@@ -387,17 +476,6 @@
       1, net::SchemefulSite(common_info.reporting_origin()).Serialize());
   statement.BindTime(2, source_time);
 
-  const int limit = delegate_->GetMaxDestinationsPerSourceSiteReportingSite();
-  DCHECK_GT(limit, 0);
-
-  std::vector<DestinationLimitRecord> records;
-  for (const auto& destination :
-       source.registration().destination_set.destinations()) {
-    records.emplace_back(destination.Serialize(), source_time,
-                         source.registration().destination_limit_priority,
-                         StoredSource::Id(kUnsetRecordId));
-  }
-
   while (statement.Step()) {
     const int64_t source_id = statement.ColumnInt64(3);
     // `source_id` should not be unset.
@@ -407,33 +485,20 @@
     if (source_id == kUnsetRecordId) {
       return base::unexpected(Error());
     }
-    records.emplace_back(/*serialized_destination=*/statement.ColumnString(0),
-                         /*time=*/statement.ColumnTime(1),
-                         /*priority=*/statement.ColumnInt64(2),
-                         StoredSource::Id(source_id));
+    AddDestination(destination_datas, /*destination=*/statement.ColumnString(0),
+                   StoredSource::Id(source_id),
+                   DestinationAttribute(/*priority=*/statement.ColumnInt64(2),
+                                        /*time=*/statement.ColumnTime(1)));
   }
 
   if (!statement.Succeeded()) {
     return base::unexpected(Error());
   }
 
-  base::ranges::sort(records, base::ranges::greater());
+  const int limit = delegate_->GetMaxDestinationsPerSourceSiteReportingSite();
+  DCHECK_GT(limit, 0);
 
-  base::flat_set<net::SchemefulSite> destination_sites;
-  std::vector<StoredSource::Id> source_ids_to_deactivate;
-
-  for (const DestinationLimitRecord& record : records) {
-    net::SchemefulSite destination =
-        net::SchemefulSite::Deserialize(record.serialized_destination);
-    if (destination_sites.size() < static_cast<size_t>(limit)) {
-      destination_sites.emplace(std::move(destination));
-    } else if (!destination_sites.contains(destination)) {
-      source_ids_to_deactivate.push_back(record.source_id);
-    }
-  }
-
-  return base::flat_set<StoredSource::Id>(std::move(source_ids_to_deactivate))
-      .extract();
+  return SelectDestinations(std::move(destination_datas), limit);
 }
 
 bool RateLimitTable::DeactivateSourcesForDestinationLimit(
diff --git a/content/browser/attribution_reporting/sql_utils.cc b/content/browser/attribution_reporting/sql_utils.cc
index 9648f29a..034fdcd 100644
--- a/content/browser/attribution_reporting/sql_utils.cc
+++ b/content/browser/attribution_reporting/sql_utils.cc
@@ -35,6 +35,7 @@
 #include "components/attribution_reporting/trigger_data_matching.mojom.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_reporting.pb.h"
+#include "content/browser/attribution_reporting/stored_source.h"
 #include "sql/statement.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
@@ -551,4 +552,8 @@
   return scopes_data;
 }
 
+void DeduplicateSourceIds(std::vector<StoredSource::Id>& ids) {
+  ids = base::flat_set<StoredSource::Id>(std::move(ids)).extract();
+}
+
 }  // namespace content
diff --git a/content/browser/attribution_reporting/sql_utils.h b/content/browser/attribution_reporting/sql_utils.h
index 4a6c1902..9e623b0 100644
--- a/content/browser/attribution_reporting/sql_utils.h
+++ b/content/browser/attribution_reporting/sql_utils.h
@@ -16,6 +16,7 @@
 #include "components/attribution_reporting/source_type.mojom-forward.h"
 #include "components/attribution_reporting/trigger_data_matching.mojom-forward.h"
 #include "content/browser/attribution_reporting/attribution_report.h"
+#include "content/browser/attribution_reporting/stored_source.h"
 #include "content/common/content_export.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
@@ -121,6 +122,8 @@
                absl::monostate>
 DeserializeAttributionScopesData(sql::Statement&, int col);
 
+void DeduplicateSourceIds(std::vector<StoredSource::Id>&);
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_SQL_UTILS_H_
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 9cef3b1..3503841d 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -1635,7 +1635,7 @@
           name: 'cars',
           owner: $1,
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -3417,7 +3417,7 @@
           name: 'cars',
           owner: 'https://invalid^&',
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -3447,7 +3447,7 @@
           owner: $1,
           prioritySignalsOverrides: "Not an object",
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -3545,7 +3545,7 @@
           sellerCapabilities: {'https://example.test': ['non-valid-capability']},
           executionMode: 'non-valid-execution-mode',
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -3883,7 +3883,7 @@
           owner: $1,
           biddingLogicURL: 'https://invalid^&',
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -3904,7 +3904,7 @@
           owner: $1,
           biddingWasmHelperURL: 'https://invalid^&',
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -3946,7 +3946,7 @@
           owner: $1,
           updateURL: 'https://invalid^&',
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -3980,7 +3980,7 @@
           owner: $1,
           dailyUpdateUrl: 'https://invalid^&',
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4015,7 +4015,7 @@
           updateURL: $1 + '/foo',
           dailyUpdateUrl:  $1 + '/bar',
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4047,7 +4047,7 @@
           owner: $1,
           trustedBiddingSignalsURL: 'https://invalid^&',
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4079,7 +4079,7 @@
           owner: $1,
           userBiddingSignals: function() {},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4111,7 +4111,7 @@
           owner: $1,
           ads: [{renderURL:"https://invalid^&"}],
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4147,7 +4147,7 @@
           owner: $1,
           ads: [{renderURL:"https://test.com", metadata:x}],
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4226,7 +4226,7 @@
           owner: $1,
           ads: [{renderURL: "https://test.com", sizeGroup: ""}],
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4259,7 +4259,7 @@
           owner: $1,
           ads: [{renderURL: "https://test.com", sizeGroup: "nonexistent"}],
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4295,7 +4295,7 @@
           adSizes: {"size_1": {"width": "50px", "height": "50px"}},
           sizeGroups: {"group_1": ["size_1"]},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4328,7 +4328,7 @@
           ads: [{renderURL: "https://test.com", sizeGroup: "group_1"}],
           sizeGroups: {"group_1": ["nonexistent"]},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4360,7 +4360,7 @@
           owner: $1,
           adComponents: [{renderURL: "https://test.com", sizeGroup: ""}],
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4394,7 +4394,7 @@
           owner: $1,
           adComponents: [{renderURL: "https://test.com", sizeGroup: "nonexistent"}],
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4430,7 +4430,7 @@
           adSizes: {"size_1": {"width": "50px", "height": "50px"}},
           sizeGroups: {"group_1": ["size_1"]},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4463,7 +4463,7 @@
           adComponents: [{renderURL: "https://test.com", sizeGroup: "group_1"}],
           sizeGroups: {"group_1": ["nonexistent"]},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4496,7 +4496,7 @@
           owner: $1,
           adSizes: {"my_size": {"width": "0px", "height": "50px"}},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4529,7 +4529,7 @@
           owner: $1,
           adSizes: {"my_size": {"width": "500px", "height": "400bad"}},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4562,7 +4562,7 @@
           owner: $1,
           adSizes: {"my_size": {"width": "500px", "height": "px"}},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4595,7 +4595,7 @@
           adSizes: {"size_1": {"width": "300px", "height": "150px"}},
           sizeGroups: {"": ["size_1"]},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4628,7 +4628,7 @@
           adSizes: {"size_1": {"width": "300px", "height": "150px"}},
           sizeGroups: {"my_group": ["nonexistant"]},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -4660,7 +4660,7 @@
           owner: $1,
           ads: [{renderURL: "https://test.com", adRenderId: "ThisIsTooLong"}],
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -5131,7 +5131,7 @@
           owner: '%s',
           %s
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -5175,7 +5175,7 @@
             aggregationCoordinatorOrigin: 'https://invalid^&'
           }
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -5206,7 +5206,7 @@
             aggregationCoordinatorOrigin: $2,
           }
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -5238,7 +5238,7 @@
             aggregationCoordinatorOrigin: 'http://coordinator.test/',
           }
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -5270,7 +5270,7 @@
             aggregationCoordinatorOrigin: 'https://coordinator.test/',
           }
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -6507,7 +6507,7 @@
           owner: '%s',
           priority: %s,
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -6529,7 +6529,7 @@
           owner: '%s',
           priorityVector: {'foo': %s},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -6551,7 +6551,7 @@
           owner: '%s',
           prioritySignalsOverrides: {'foo': %s},
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
@@ -10886,7 +10886,7 @@
               owner: $1,
               ads: [{name:"foo",renderURL:$2}],
             },
-            /*joinDurationSec=*/1);
+            /*joinDurationSec=*/1000);
       } catch (e) {
         return e.toString();
       }
@@ -16006,7 +16006,7 @@
           userBiddingSignals: 1,
           ads: [{renderURL:"https://example.com/render", metadata:2}],
         },
-        /*joinDurationSec=*/1);
+        /*joinDurationSec=*/1000);
   } catch (e) {
     return e.toString();
   }
diff --git a/content/browser/interest_group/trusted_signals_cache_impl.cc b/content/browser/interest_group/trusted_signals_cache_impl.cc
index 654177e..ec79fb29 100644
--- a/content/browser/interest_group/trusted_signals_cache_impl.cc
+++ b/content/browser/interest_group/trusted_signals_cache_impl.cc
@@ -26,6 +26,7 @@
 #include "base/timer/timer.h"
 #include "base/types/expected.h"
 #include "base/unguessable_token.h"
+#include "base/values.h"
 #include "content/browser/interest_group/bidding_and_auction_server_key_fetcher.h"
 #include "content/browser/interest_group/trusted_signals_fetcher.h"
 #include "content/services/auction_worklet/public/mojom/trusted_signals_cache.mojom.h"
diff --git a/content/browser/interest_group/trusted_signals_cache_impl_unittest.cc b/content/browser/interest_group/trusted_signals_cache_impl_unittest.cc
index a572c92b6..b382321 100644
--- a/content/browser/interest_group/trusted_signals_cache_impl_unittest.cc
+++ b/content/browser/interest_group/trusted_signals_cache_impl_unittest.cc
@@ -471,7 +471,7 @@
     base::TimeDelta ttl = base::Hours(1)) {
   TrustedSignalsFetcher::CompressionGroupResult result;
   result.compression_group_data =
-      std::vector<std::uint8_t>(body.begin(), body.end());
+      base::Value::BlobStorage(body.begin(), body.end());
   result.compression_scheme = compression_scheme;
   result.ttl = ttl;
   return result;
diff --git a/content/browser/interest_group/trusted_signals_fetcher.cc b/content/browser/interest_group/trusted_signals_fetcher.cc
index d9e31a6c..d4b06d9e 100644
--- a/content/browser/interest_group/trusted_signals_fetcher.cc
+++ b/content/browser/interest_group/trusted_signals_fetcher.cc
@@ -431,8 +431,10 @@
   CompressionGroupResultMap compression_groups_out;
   for (auto& compression_group : *compression_groups) {
     int compression_group_id;
-    auto compression_group_result =
-        ParseCompressionGroup(compression_group, compression_group_id);
+    // This consumes each value of the list, to avoid having to copy the
+    // contents of each compression group.
+    auto compression_group_result = ParseCompressionGroup(
+        std::move(compression_group), compression_group_id);
 
     if (!compression_group_result.has_value()) {
       return base::unexpected(std::move(compression_group_result).error());
@@ -453,15 +455,14 @@
 
 base::expected<TrustedSignalsFetcher::CompressionGroupResult, std::string>
 TrustedSignalsFetcher::ParseCompressionGroup(
-    const base::Value& compression_group_value,
+    base::Value compression_group_value,
     int& compression_group_id) {
   if (!compression_group_value.is_dict()) {
     return base::unexpected(CreateError(
         base::StringPrintf("Compression group is not of type map")));
   }
 
-  const base::Value::Dict& compression_group_dict =
-      compression_group_value.GetDict();
+  base::Value::Dict& compression_group_dict = compression_group_value.GetDict();
   std::optional<int> compression_group_id_opt =
       compression_group_dict.FindInt("compressionGroupId");
   if (!compression_group_id_opt.has_value() || *compression_group_id_opt < 0) {
@@ -484,7 +485,7 @@
     ttl = base::Milliseconds(std::max(0, ttl_ms_value->GetInt()));
   }
 
-  const auto* content = compression_group_dict.FindBlob("content");
+  auto* content = compression_group_dict.FindBlob("content");
   if (!content) {
     return base::unexpected(CreateError(base::StringPrintf(
         "Compression group %i missing binary string \"content\"",
@@ -495,11 +496,7 @@
 
   CompressionGroupResult result;
   result.compression_scheme = compression_scheme_;
-  // TODO(crbug.com/333445540): This copy is only necessary because Value::Dict
-  // does not allow blob/binary types to be moved, unlike std::strings. Modify
-  // base::Value to allow that, and switch this code to take advantage of the
-  // capability.
-  result.compression_group_data = *content;
+  result.compression_group_data = std::move(*content);
   result.ttl = ttl;
   return result;
 }
diff --git a/content/browser/interest_group/trusted_signals_fetcher.h b/content/browser/interest_group/trusted_signals_fetcher.h
index 7660181..9a0d9589 100644
--- a/content/browser/interest_group/trusted_signals_fetcher.h
+++ b/content/browser/interest_group/trusted_signals_fetcher.h
@@ -121,7 +121,7 @@
     auction_worklet::mojom::TrustedSignalsCompressionScheme compression_scheme;
 
     // The still-compressed data for the compression group.
-    std::vector<uint8_t> compression_group_data;
+    base::Value::BlobStorage compression_group_data;
 
     // Time until the response expires.
     base::TimeDelta ttl;
@@ -193,7 +193,7 @@
   // passed in value. On failure, leaves `compression_group_id` alone, and
   // returns a string.
   base::expected<CompressionGroupResult, std::string> ParseCompressionGroup(
-      const base::Value& compression_group_value,
+      base::Value compression_group_value,
       int& compression_group_id);
 
   // Returns a string error message, prefixing the passed in message with the
diff --git a/content/browser/interest_group/trusted_signals_fetcher_unittest.cc b/content/browser/interest_group/trusted_signals_fetcher_unittest.cc
index 7fb7f7c..7a304a5 100644
--- a/content/browser/interest_group/trusted_signals_fetcher_unittest.cc
+++ b/content/browser/interest_group/trusted_signals_fetcher_unittest.cc
@@ -80,7 +80,7 @@
     base::TimeDelta ttl) {
   TrustedSignalsFetcher::CompressionGroupResult out;
   out.compression_scheme = compression_scheme;
-  out.compression_group_data = std::vector<uint8_t>(
+  out.compression_group_data = base::Value::BlobStorage(
       compression_group_data.begin(), compression_group_data.end());
   out.ttl = ttl;
   return out;
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index ae65c067..4030d23 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -4441,6 +4441,56 @@
   EXPECT_EQ(GURL("about:blank"), web_contents_b->GetLastCommittedURL());
 }
 
+// Check that when RenderProcessHostImpl::DisableRefCounts is called while a
+// NavigationStateKeepAlive exists, the navigation still succeeds. This is a
+// regression test for crbug.com/348150830.
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
+                       DisableRefCountsWhileKeepAliveExists) {
+  GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // This test needs the browser process to call DisableRefCounts after the form
+  // submission's NavigationStateKeepAlive is created and before the task that
+  // sends the BeginNavigation IPC. To do this, use EvalJS to return a string to
+  // the test framework between those two renderer-side tasks, allowing the
+  // browser process to reset the counts before the BeginNavigation IPC is
+  // received and the NavigationStateKeepAlive is destroyed.
+  std::string expected_str("Placeholder value");
+  std::string js_str = base::StringPrintf(
+      "f = document.createElement('form');"
+      "f.action = 'about:blank';"
+      "document.body.appendChild(f);"
+      "f.submit();"
+      "'%s';",
+      expected_str.c_str());
+
+  TestNavigationObserver observer(shell()->web_contents());
+  EXPECT_EQ(expected_str, EvalJs(shell(), js_str).ExtractString());
+
+  // Expect at this point that a NavigationStateKeepAlive has been created for
+  // the form submission.
+  NavigationStateKeepAlive* keep_alive =
+      current_frame_host()->GetStoragePartition()->GetNavigationStateKeepAlive(
+          current_frame_host()->GetFrameToken());
+  ASSERT_TRUE(keep_alive);
+
+  // Disable ref counts on the process, which resets all ref counts to 0. This
+  // seems to happen in practice in https://crbug.com/348150830 when a
+  // BrowserContext is closed before all of its frames are properly cleaned up,
+  // but the exact repro steps for this aren't known, so simulate this behavior
+  // with an explicit DisableRefCounts() call.
+  current_frame_host()->GetProcess()->DisableRefCounts();
+
+  // Wait for the navigation to complete. At that point, the
+  // NavigationStateKeepAlive goes away, which can possibly decrement the
+  // associated ref count. Since DisableRefCounts() was called, the ref count
+  // should not be further decremented, and the navigation should complete
+  // successfully.
+  observer.Wait();
+  EXPECT_TRUE(observer.last_navigation_succeeded());
+  EXPECT_TRUE(current_frame_host()->GetLastCommittedURL().IsAboutBlank());
+}
+
 using MediaNavigationBrowserTest = NavigationBaseBrowserTest;
 
 // Media navigations synchronously complete the time of the `CommitNavigation`
diff --git a/content/browser/renderer_host/document_associated_data.h b/content/browser/renderer_host/document_associated_data.h
index 47d3db5b..eb99b0c 100644
--- a/content/browser/renderer_host/document_associated_data.h
+++ b/content/browser/renderer_host/document_associated_data.h
@@ -68,6 +68,11 @@
   bool dom_content_loaded() const { return dom_content_loaded_; }
   void MarkDomContentLoaded() { dom_content_loaded_ = true; }
 
+  // Indicates whether a discard request has been dispatched for the current
+  // document.
+  bool is_discarded() const { return is_discarded_; }
+  void MarkDiscarded() { is_discarded_ = true; }
+
   // Prerender2:
   //
   // The URL that `blink.mojom.LocalFrameHost::DidFinishLoad()` passed to
@@ -157,6 +162,7 @@
   const blink::DocumentToken token_;
   std::unique_ptr<PageImpl> owned_page_;
   bool dom_content_loaded_ = false;
+  bool is_discarded_ = false;
   std::optional<GURL> pending_did_finish_load_url_for_prerendering_;
   std::vector<raw_ptr<internal::DocumentServiceBase, VectorExperimental>>
       services_;
diff --git a/content/browser/renderer_host/frame_tree.cc b/content/browser/renderer_host/frame_tree.cc
index 6b1932a..3cb7ab5 100644
--- a/content/browser/renderer_host/frame_tree.cc
+++ b/content/browser/renderer_host/frame_tree.cc
@@ -736,30 +736,27 @@
   if (base::Contains(render_view_host_map_, id)) {
     // TODO(https://crbug.com/354382462): Remove crash keys once investigation
     // is done.
-    static auto* const is_registered_key = base::debug::AllocateCrashKeyString(
-        "is_registered", base::debug::CrashKeySize::Size32);
-    base::debug::SetCrashKeyString(
-        is_registered_key,
-        rvh->is_registered_with_frame_tree() ? "true" : "false");
-    static auto* const renderer_view_created_key =
-        base::debug::AllocateCrashKeyString("renderer_view_created",
-                                            base::debug::CrashKeySize::Size32);
-    base::debug::SetCrashKeyString(
-        renderer_view_created_key,
-        rvh->renderer_view_created() ? "true" : "false");
-    static auto* const main_frame_routing_id_key =
-        base::debug::AllocateCrashKeyString("rvh_main_routing_id",
-                                            base::debug::CrashKeySize::Size32);
-    base::debug::SetCrashKeyString(
-        main_frame_routing_id_key,
-        base::NumberToString(rvh->main_frame_routing_id()));
-    static auto* const root_routing_id_key =
-        base::debug::AllocateCrashKeyString("root_routing_id",
-                                            base::debug::CrashKeySize::Size32);
-    base::debug::SetCrashKeyString(
-        root_routing_id_key,
-        base::NumberToString(root()->current_frame_host()->GetRoutingID()));
-    CHECK(false);
+    SCOPED_CRASH_KEY_BOOL("rvh-double", "renderer_view_created",
+                          rvh->renderer_view_created());
+    SCOPED_CRASH_KEY_NUMBER("rvh-double", "mapped_rvh_main_id",
+                            render_view_host_map_[id]->main_frame_routing_id());
+    SCOPED_CRASH_KEY_NUMBER("rvh-double", "passed_rvh_main_id",
+                            rvh->main_frame_routing_id());
+    SCOPED_CRASH_KEY_NUMBER("rvh-double", "root_routing_id",
+                            root()->current_frame_host()->GetRoutingID());
+    SCOPED_CRASH_KEY_NUMBER(
+        "rvh-double", "map_rvh_ptr",
+        reinterpret_cast<size_t>(render_view_host_map_[id]));
+    SCOPED_CRASH_KEY_NUMBER(
+        "rvh-double", "map_rvh_bfcache",
+        render_view_host_map_[id]->is_in_back_forward_cache());
+    SCOPED_CRASH_KEY_NUMBER("rvh-double", "passed_rvh_ptr",
+                            reinterpret_cast<size_t>(rvh));
+    SCOPED_CRASH_KEY_NUMBER("rvh-double", "passed_rvh_bfcache",
+                            rvh->is_in_back_forward_cache());
+    SCOPED_CRASH_KEY_NUMBER("rvh-double", "frame_tree_primary", is_primary());
+    CHECK_EQ(rvh, render_view_host_map_[id]);
+    base::debug::DumpWithoutCrashing();
   }
   render_view_host_map_[id] = rvh;
   rvh->set_is_registered_with_frame_tree(true);
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index f4ee61511..77123ee 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -2007,18 +2007,6 @@
       ad_auction_headers_eligible_ = true;
       headers.SetHeader(kAdAuctionRequestHeaderKey, "?1");
     }
-
-    // Partitioned popins are special modal popups that are partitioned as
-    // though they were an iframe embedded in the opener. All main-frame
-    // navigations and redirects must set a request header to notify the loaded
-    // site they are in a partitioned popin and not a standard popup.
-    // See https://explainers-by-googlers.github.io/partitioned-popins/
-    if (frame_tree_node->IsOutermostMainFrame() &&
-        frame_tree_node->current_frame_host()
-            ->delegate()
-            ->PartitionedPopinOpener()) {
-      headers.SetHeader("Sec-Popin-Context", "partitioned");
-    }
   }
 
   begin_params_->headers = headers.ToString();
diff --git a/content/browser/renderer_host/navigation_throttle_runner.cc b/content/browser/renderer_host/navigation_throttle_runner.cc
index 0381c06..ac619c1c 100644
--- a/content/browser/renderer_host/navigation_throttle_runner.cc
+++ b/content/browser/renderer_host/navigation_throttle_runner.cc
@@ -22,6 +22,7 @@
 #include "content/browser/renderer_host/mixed_content_navigation_throttle.h"
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/renderer_host/navigator_delegate.h"
+#include "content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.h"
 #include "content/browser/renderer_host/renderer_cancellation_throttle.h"
 #include "content/browser/renderer_host/subframe_history_navigation_throttle.h"
 #include "content/public/browser/navigation_handle.h"
@@ -263,6 +264,11 @@
         BackForwardCacheSubframeNavigationThrottle::MaybeCreateThrottleFor(
             request));
   }
+
+  // Add a throttle to manage top-frame navigations from a partitioned popin.
+  // See https://explainers-by-googlers.github.io/partitioned-popins/
+  AddThrottle(
+      PartitionedPopinsNavigationThrottle::MaybeCreateThrottleFor(request));
   // DO NOT ADD any throttles after this line.
 
   // Insert all testing NavigationThrottles last.
diff --git a/content/browser/renderer_host/partitioned_popins/DEPS b/content/browser/renderer_host/partitioned_popins/DEPS
new file mode 100644
index 0000000..0a6990e
--- /dev/null
+++ b/content/browser/renderer_host/partitioned_popins/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+content/browser/web_contents/web_contents_impl.h",
+]
diff --git a/content/browser/renderer_host/partitioned_popins/OWNERS b/content/browser/renderer_host/partitioned_popins/OWNERS
new file mode 100644
index 0000000..feb0714
--- /dev/null
+++ b/content/browser/renderer_host/partitioned_popins/OWNERS
@@ -0,0 +1,3 @@
+arichiv@chromium.org
+johannhof@chromium.org
+sandormajor@google.com
diff --git a/content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.cc b/content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.cc
new file mode 100644
index 0000000..865015ba
--- /dev/null
+++ b/content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.cc
@@ -0,0 +1,66 @@
+// 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 "content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.h"
+
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/navigation_handle.h"
+#include "services/network/public/cpp/resource_request.h"
+
+namespace content {
+
+// static
+std::unique_ptr<PartitionedPopinsNavigationThrottle>
+PartitionedPopinsNavigationThrottle::MaybeCreateThrottleFor(
+    NavigationHandle* navigation_handle) {
+  CHECK(navigation_handle);
+  // Only the outermost frame in a partitioned popin needs the throttle.
+  // See https://explainers-by-googlers.github.io/partitioned-popins/
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(navigation_handle->GetWebContents());
+  if (navigation_handle->IsInOutermostMainFrame() && web_contents &&
+      web_contents->IsPartitionedPopin()) {
+    return base::WrapUnique(
+        new PartitionedPopinsNavigationThrottle(navigation_handle));
+  }
+  return nullptr;
+}
+
+const char* PartitionedPopinsNavigationThrottle::GetNameForLogging() {
+  return "PartitionedPopinsNavigationThrottle";
+}
+
+NavigationThrottle::ThrottleCheckResult
+PartitionedPopinsNavigationThrottle::WillStartRequest() {
+  // Partitioned popin top-frames cannot navigate to HTTP pages, if this occurs
+  // we need to block the request.
+  // See https://explainers-by-googlers.github.io/partitioned-popins/
+  if (!navigation_handle()->GetURL().SchemeIs(url::kHttpsScheme)) {
+    return BLOCK_REQUEST;
+  }
+  // Partitioned popins are special modal popups that are partitioned as
+  // though they were an iframe embedded in the opener. All main-frame
+  // navigations and redirects must set a request header to notify the loaded
+  // site they are in a partitioned popin and not a standard popup.
+  // See https://explainers-by-googlers.github.io/partitioned-popins/
+  navigation_handle()->SetRequestHeader("Sec-Popin-Context", "partitioned");
+  return PROCEED;
+}
+
+NavigationThrottle::ThrottleCheckResult
+PartitionedPopinsNavigationThrottle::WillRedirectRequest() {
+  // Partitioned popin top-frames cannot redirect to HTTP pages, if this occurs
+  // we need to block the request.
+  // See https://explainers-by-googlers.github.io/partitioned-popins/
+  if (!navigation_handle()->GetURL().SchemeIs(url::kHttpsScheme)) {
+    return BLOCK_REQUEST;
+  }
+  return PROCEED;
+}
+
+PartitionedPopinsNavigationThrottle::PartitionedPopinsNavigationThrottle(
+    NavigationHandle* navigation_handle)
+    : NavigationThrottle(navigation_handle) {}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.h b/content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.h
new file mode 100644
index 0000000..50c3302
--- /dev/null
+++ b/content/browser/renderer_host/partitioned_popins/partitioned_popins_navigation_throttle.h
@@ -0,0 +1,34 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_PARTITIONED_POPINS_PARTITIONED_POPINS_NAVIGATION_THROTTLE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_PARTITIONED_POPINS_PARTITIONED_POPINS_NAVIGATION_THROTTLE_H_
+
+#include "content/common/content_export.h"
+#include "content/public/browser/navigation_throttle.h"
+
+namespace content {
+
+// This throttle should be attached to all top-frame navigations for partitioned
+// popins to handle blocks and headers.
+// See https://explainers-by-googlers.github.io/partitioned-popins/
+class CONTENT_EXPORT PartitionedPopinsNavigationThrottle
+    : public NavigationThrottle {
+ public:
+  static std::unique_ptr<PartitionedPopinsNavigationThrottle>
+  MaybeCreateThrottleFor(NavigationHandle* navigation_handle);
+
+  // NavigationThrottle
+  const char* GetNameForLogging() override;
+  NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
+  NavigationThrottle::ThrottleCheckResult WillRedirectRequest() override;
+
+ private:
+  explicit PartitionedPopinsNavigationThrottle(
+      NavigationHandle* navigation_handle);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_PARTITIONED_POPINS_PARTITIONED_POPINS_NAVIGATION_THROTTLE_H_
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index a02bcbe..99b4e6a 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -199,6 +199,10 @@
   return false;
 }
 
+bool RenderFrameHostDelegate::IsPartitionedPopin() const {
+  return false;
+}
+
 RenderFrameHostImpl* RenderFrameHostDelegate::PartitionedPopinOpener() const {
   return nullptr;
 }
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index 17ca6ff..7e692e6b 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -734,9 +734,13 @@
   virtual void DraggableRegionsChanged(
       const std::vector<blink::mojom::DraggableRegionPtr>& regions) {}
 
-  // Whether this window was initially opened as a new popup.
+  // Whether the containing window was initially opened as a new popup.
   virtual bool IsPopup() const;
 
+  // If the containing window was opened as a new partitioned popin.
+  // See https://explainers-by-googlers.github.io/partitioned-popins/
+  virtual bool IsPartitionedPopin() const;
+
   // If this window was opened as a new partitioned popin this will be the
   // frame of the opener. This will only have a value if `is_popup_` is true.
   // See https://explainers-by-googlers.github.io/partitioned-popins/
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 6f9e6c2a..56be13f 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1381,7 +1381,7 @@
 };
 
 WindowProxyPageContext GetWindowProxyPageContext(RenderFrameHostImpl* frame) {
-  if (frame->delegate()->PartitionedPopinOpener()) {
+  if (frame->delegate()->IsPartitionedPopin()) {
     return WindowProxyPageContext::kPartitionedPopin;
   } else if (frame->delegate()->IsPopup()) {
     return WindowProxyPageContext::kPopup;
@@ -4518,10 +4518,10 @@
   // If this frame is in a partitioned popin, we consider the opener's top-frame
   // to be this frame's top-frame as long as we aren't in a fenced-frame.
   // See: https://explainers-by-googlers.github.io/partitioned-popins/
-  RenderFrameHostImpl* partitioned_popin_opener =
-      delegate_->PartitionedPopinOpener();
-  if (partitioned_popin_opener && !IsNestedWithinFencedFrame()) {
-    return partitioned_popin_opener->GetMainFrame()->GetLastCommittedOrigin();
+  if (delegate_->IsPartitionedPopin() && !IsNestedWithinFencedFrame()) {
+    return delegate_->PartitionedPopinOpener()
+        ->GetMainFrame()
+        ->GetLastCommittedOrigin();
   }
 
   if (is_main_frame()) {
@@ -4561,7 +4561,7 @@
   // See: https://explainers-by-googlers.github.io/partitioned-popins/
   net::IsolationInfo::RequestType request_type =
       (is_main_frame() &&
-       (!delegate()->PartitionedPopinOpener() || IsNestedWithinFencedFrame()))
+       (!delegate()->IsPartitionedPopin() || IsNestedWithinFencedFrame()))
           ? net::IsolationInfo::RequestType::kMainFrame
           : net::IsolationInfo::RequestType::kSubFrame;
   return ComputeIsolationInfoInternal(url::Origin::Create(destination),
@@ -4719,9 +4719,9 @@
   // to be this frame's top-frame as long as we aren't in a fenced-frame. We
   // must add all intermediate frames to ensure proper StorageKey calculation.
   // See: https://explainers-by-googlers.github.io/partitioned-popins/
-  RenderFrameHostImpl* partitioned_popin_opener =
-      delegate()->PartitionedPopinOpener();
-  if (partitioned_popin_opener && !IsNestedWithinFencedFrame()) {
+  if (delegate()->IsPartitionedPopin() && !IsNestedWithinFencedFrame()) {
+    RenderFrameHostImpl* partitioned_popin_opener =
+        delegate()->PartitionedPopinOpener();
     while (partitioned_popin_opener) {
       ancestor_chain.push_back(partitioned_popin_opener);
       partitioned_popin_opener = partitioned_popin_opener->parent_;
@@ -5695,7 +5695,14 @@
   } else {
     // RenderDocument: After a local<->local swap, this function is called with
     // a null |proxy|.
-    CHECK(ShouldChangeRenderFrameHostOnSameSiteNavigation());
+    // It may be the case that two navigations are queued up. Both may initially
+    // see `ShouldChangeRenderFrameHostOnSameSiteNavigation()` return true,
+    // however after the first one commits the RFH will change. When the second
+    // navigation commits `ShouldChangeRenderFrameHostOnSameSiteNavigation()`
+    // may no longer return true.
+    CHECK_EQ(
+        GetSiteInstance()->group(),
+        frame_tree_node_->current_frame_host()->GetSiteInstance()->group());
 
     // The unload handlers already ran for this document during the
     // local<->local swap. Hence, there is no need to send
@@ -7193,9 +7200,11 @@
 void RenderFrameHostImpl::UpdateTitle(
     const std::optional<::std::u16string>& title,
     base::i18n::TextDirection title_direction) {
-  // This message should only be sent for top-level frames.
-  if (!is_main_frame())
+  // This message should only be sent for top-level frames. Suppress title
+  // updates if the message was sent for a discarded document.
+  if (!is_main_frame() || document_associated_data_->is_discarded()) {
     return;
+  }
 
   std::u16string received_title;
   if (title.has_value())
@@ -7624,7 +7633,7 @@
 
   // This may be called when the main frame document is replaced with the empty
   // document during discard. Suppress document load notifications in this case.
-  if (was_discarded_) {
+  if (document_associated_data_->is_discarded()) {
     return;
   }
 
@@ -8836,7 +8845,7 @@
       mojo::ReportBadMessage("Partitioned popins not permitted.");
       return;
     }
-    if (delegate()->PartitionedPopinOpener()) {
+    if (delegate()->IsPartitionedPopin()) {
       mojo::ReportBadMessage("Partitioned popins cannot open their own popin.");
       return;
     }
@@ -9069,9 +9078,10 @@
   // the renderer to properly conduct checks.
   // See https://explainers-by-googlers.github.io/partitioned-popins/
   blink::mojom::PartitionedPopinParamsPtr partitioned_popin_params = nullptr;
-  RenderFrameHostImpl* partitioned_popin_opener =
-      new_main_rfh->delegate()->PartitionedPopinOpener();
-  if (partitioned_popin_opener && !IsNestedWithinFencedFrame()) {
+  if (new_main_rfh->delegate()->IsPartitionedPopin() &&
+      !IsNestedWithinFencedFrame()) {
+    RenderFrameHostImpl* partitioned_popin_opener =
+        new_main_rfh->delegate()->PartitionedPopinOpener();
     partitioned_popin_params = blink::mojom::PartitionedPopinParams::New(
         partitioned_popin_opener->ComputeTopFrameOrigin(
             partitioned_popin_opener->GetLastCommittedOrigin()),
@@ -11804,7 +11814,7 @@
 }
 
 void RenderFrameHostImpl::DiscardFrame() {
-  was_discarded_ = true;
+  document_associated_data_->MarkDiscarded();
   BackForwardCache::DisableForRenderFrameHost(
       this, BackForwardCacheDisable::DisabledReason(
                 BackForwardCacheDisable::DisabledReasonId::kDiscarded));
@@ -17283,7 +17293,7 @@
     const {
   // Reloading from a discarded state will result in a same-site navigation. In
   // these cases we should always create a new RFH for the navigation.
-  if (was_discarded_) {
+  if (document_associated_data_->is_discarded()) {
     return true;
   }
   return ShouldCreateNewRenderFrameHostOnSameSiteNavigation(
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 667a9dbd..a3c43d2 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -4504,8 +4504,7 @@
   std::unique_ptr<input::TimeoutMonitor> close_timeout_;
 
   // Returns whether the tab was previously discarded.
-  // This is passed to CommitNavigationParams in NavigationRequest or is set on
-  // the outermost main frame when its tree has been discarded.
+  // This is passed to CommitNavigationParams in NavigationRequest.
   bool was_discarded_ = false;
 
   // Indicates whether this RenderFrameHost is in the process of loading a
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 84bf098..9e5567a 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -318,6 +318,8 @@
 const char kSiteProcessMapKeyName[] = "content_site_process_map";
 
 const void* const kProcessPerSiteUmaLoggedKey = &kProcessPerSiteUmaLoggedKey;
+const void* const kSubframeProcessReuseOverLimitUmaLoggedKey =
+    &kSubframeProcessReuseOverLimitUmaLoggedKey;
 
 RenderProcessHost::AnalyzeHungRendererFunction g_analyze_hung_renderer =
     nullptr;
@@ -479,7 +481,7 @@
   // A few definitions:
   // PMF - the private memory footprint of the memory allocated by the
   //       process excluding any shared memory segments.
-  // size_per_top_level_frame - the estimated size of a top level frame,
+  // size_per_top_level_frame - the estimated size of each top level frame,
   //       assuming each top level frame is around the same size. This
   //       is calculated by dividing the PMF by the number of top level frames
   //       in the process. This algorithm treats any OOPIFs being equally
@@ -498,7 +500,7 @@
   //       We should try to keep the private memory less than this value.
   //
   // The algorithm:
-  //    Should Use Same Process = (PMF + estimated_size) < process_memory_limit
+  //    Has Enough Room = (PMF + estimated_size) < process_memory_limit
   uint64_t private_memory_footprint = host->GetPrivateMemoryFootprint();
 
   // Zero indicates we didn't obtain a PMF so we should just deny it, because
@@ -547,16 +549,27 @@
                                     SiteInstanceImpl* site_instance,
                                     ProcessReusePolicy process_reuse_policy) {
   if (process_reuse_policy !=
-      ProcessReusePolicy::
-          REUSE_PENDING_OR_COMMITTED_SITE_WITH_MAIN_FRAME_THRESHOLD) {
+          ProcessReusePolicy::
+              REUSE_PENDING_OR_COMMITTED_SITE_WITH_MAIN_FRAME_THRESHOLD &&
+      process_reuse_policy !=
+          ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_SUBFRAME) {
+    return true;
+  }
+
+  if (process_reuse_policy ==
+          ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_SUBFRAME &&
+      !base::FeatureList::IsEnabled(
+          features::kSubframeProcessReuseThresholds)) {
     return true;
   }
 
   size_t main_frame_count = 0;
+  size_t total_frame_count = 0;
   bool devtools_attached = false;
   host->ForEachRenderFrameHost(
-      [&main_frame_count,
+      [&main_frame_count, &total_frame_count,
        &devtools_attached](RenderFrameHost* render_frame_host) {
+        ++total_frame_count;
         if (static_cast<RenderFrameHostImpl*>(render_frame_host)
                 ->IsOutermostMainFrame()) {
           ++main_frame_count;
@@ -568,24 +581,61 @@
         }
       });
 
-  // If a threshold is specified, don't reuse `host` if it already hosts more
-  // main frames (including BFCached and prerendered) than the threshold.
-  size_t main_frame_threshold = base::checked_cast<size_t>(
-      features::kProcessPerSiteMainFrameThreshold.Get());
-  if (main_frame_count >= main_frame_threshold) {
-    return false;
+  if (process_reuse_policy ==
+      ProcessReusePolicy::
+          REUSE_PENDING_OR_COMMITTED_SITE_WITH_MAIN_FRAME_THRESHOLD) {
+    // If a threshold is specified, don't reuse `host` if it already hosts more
+    // main frames (including BFCached and prerendered) than the threshold.
+    size_t main_frame_threshold = base::checked_cast<size_t>(
+        features::kProcessPerSiteMainFrameThreshold.Get());
+    if (main_frame_count >= main_frame_threshold) {
+      return false;
+    }
+
+    // Don't reuse `host` if DevTools is attached to any frame in that process
+    // since DevTools doesn't work well when a renderer has multiple main
+    // frames.
+    // TODO(crbug.com/40269649): This is just a heuristic and won't work if
+    // DevTools is attached later, and hence this should be eventually removed
+    // and fixed properly in the renderer process.
+    if (devtools_attached) {
+      return false;
+    }
+
+    return HasEnoughMemoryForAnotherMainFrame(host, main_frame_count);
   }
 
-  // Don't reuse `host` if DevTools is attached to any frame in that process
-  // since DevTools doesn't work well when a renderer has multiple main frames.
-  // TODO(crbug.com/40269649): This is just a heuristic and won't work if
-  // DevTools is attached later, and hence this should be eventually removed and
-  // fixed properly in the renderer process.
-  if (devtools_attached) {
-    return false;
+  DCHECK_EQ(process_reuse_policy,
+            ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_SUBFRAME);
+
+  // For subframe process reuse, simply check if the `host` has already exceeded
+  // the memory threshold to decide whether it should be reused for a new
+  // subframe. This simple heuristic should suffice if the memory threshold is
+  // already set conservatively (below the threshold that would actually lead to
+  // OOMs), and avoids the complexity of trying to estimate the size of each
+  // main frame and subframe in the process, how many extra same-site frames
+  // would be necessarily added to `host` later if the current frame were
+  // allowed to reuse it (e.g., as its subframes), etc.
+  uint64_t process_memory_limit = base::saturated_cast<uint64_t>(
+      features::kSubframeProcessReuseMemoryThreshold.Get());
+  if (host->GetPrivateMemoryFootprint() < process_memory_limit) {
+    return true;
   }
 
-  return HasEnoughMemoryForAnotherMainFrame(host, main_frame_count);
+  // Record the total number of frames at the time the subframe memory threshold
+  // is exceeded. A process may enter and exit this condition multiple times,
+  // so to avoid over-recording only record this the first time the threshold
+  // is exceeded.
+  if (!host->GetUserData(kSubframeProcessReuseOverLimitUmaLoggedKey)) {
+    base::UmaHistogramCounts1000(
+        "BrowserRenderProcessHost.SubframeProcessReuseThreshold."
+        "TotalFrames",
+        total_frame_count);
+    host->SetUserData(kSubframeProcessReuseOverLimitUmaLoggedKey,
+                      std::make_unique<base::SupportsUserData::Data>());
+  }
+
+  return false;
 }
 
 // The following class is used to track the sites each RenderProcessHost is
@@ -2575,10 +2625,12 @@
 }
 
 void RenderProcessHostImpl::IncrementNavigationStateKeepAliveCount() {
+  CHECK(!are_ref_counts_disabled_);
   navigation_state_keepalive_count_++;
 }
 
 void RenderProcessHostImpl::DecrementNavigationStateKeepAliveCount() {
+  CHECK(!are_ref_counts_disabled_);
   CHECK_GT(navigation_state_keepalive_count_, 0);
   navigation_state_keepalive_count_--;
   if (navigation_state_keepalive_count_ == 0) {
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 6876e6c..f130879c 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -606,10 +606,10 @@
   // We must send access information relative to the popin opener in order for
   // the renderer to properly conduct checks.
   // See https://explainers-by-googlers.github.io/partitioned-popins/
-  RenderFrameHostImpl* partitioned_popin_opener =
-      frame_tree_->GetMainFrame()->delegate()->PartitionedPopinOpener();
-  if (partitioned_popin_opener &&
-      !frame_tree_->GetMainFrame()->IsNestedWithinFencedFrame()) {
+  if (!frame_tree_->GetMainFrame()->IsNestedWithinFencedFrame() &&
+      frame_tree_->GetMainFrame()->delegate()->IsPartitionedPopin()) {
+    RenderFrameHostImpl* partitioned_popin_opener =
+        frame_tree_->GetMainFrame()->delegate()->PartitionedPopinOpener();
     params->partitioned_popin_params =
         blink::mojom::PartitionedPopinParams::New(
             partitioned_popin_opener->ComputeTopFrameOrigin(
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index 3b0e5d58..e4ff2c1e7 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -92,7 +92,7 @@
 }
 
 ui::WindowShowState RenderWidgetHostDelegate::GetWindowShowState() {
-  return ui::WindowShowState::SHOW_STATE_DEFAULT;
+  return ui::SHOW_STATE_DEFAULT;
 }
 
 blink::mojom::DevicePostureProvider*
diff --git a/content/browser/site_instance_group.cc b/content/browser/site_instance_group.cc
index 5474d64b..6794d4f8 100644
--- a/content/browser/site_instance_group.cc
+++ b/content/browser/site_instance_group.cc
@@ -82,8 +82,10 @@
 
 void SiteInstanceGroup::IncrementKeepAliveCount() {
   keep_alive_count_++;
-  static_cast<RenderProcessHostImpl*>(process())
-      ->IncrementNavigationStateKeepAliveCount();
+  auto* rphi = static_cast<RenderProcessHostImpl*>(process());
+  if (!rphi->AreRefCountsDisabled()) {
+    rphi->IncrementNavigationStateKeepAliveCount();
+  }
 }
 
 void SiteInstanceGroup::DecrementKeepAliveCount() {
@@ -93,8 +95,10 @@
       observer.KeepAliveCountIsZero(this);
     }
   }
-  static_cast<RenderProcessHostImpl*>(process())
-      ->DecrementNavigationStateKeepAliveCount();
+  auto* rphi = static_cast<RenderProcessHostImpl*>(process());
+  if (!rphi->AreRefCountsDisabled()) {
+    rphi->DecrementNavigationStateKeepAliveCount();
+  }
 }
 
 bool SiteInstanceGroup::IsRelatedSiteInstanceGroup(SiteInstanceGroup* group) {
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 0869099..39ff0bd 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -13763,6 +13763,94 @@
   ASSERT_NE(main_frame->GetProcess(), second_frame->GetProcess());
 }
 
+// Helper class to enable subframe process reuse thresholds and set the total
+// allowed memory limit to 8 bytes.
+class SitePerProcessWithSubframeProcessReuseThresholdsTest
+    : public SitePerProcessBrowserTestBase,
+      public ::testing::WithParamInterface<std::string> {
+ public:
+  SitePerProcessWithSubframeProcessReuseThresholdsTest() {
+    size_t total_memory_limit = 8;
+    base::FieldTrialParams params = {
+        {"SubframeProcessReuseMemoryThreshold",
+         base::StringPrintf("%zu", total_memory_limit)}};
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        features::kSubframeProcessReuseThresholds, params);
+  }
+  ~SitePerProcessWithSubframeProcessReuseThresholdsTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Verify that a subframe will only reuse an existing process if adding
+// another subframe to that process won't exceed the memory threshold.
+IN_PROC_BROWSER_TEST_P(SitePerProcessWithSubframeProcessReuseThresholdsTest,
+                       SubframeReuseRespectsMemoryThreshold) {
+  base::HistogramTester histograms;
+
+  // Start with a simple a(b) page.
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  RenderFrameHostImpl* main_frame1 =
+      static_cast<WebContentsImpl*>(shell()->web_contents())
+          ->GetPrimaryMainFrame();
+  RenderFrameHostImpl* subframe1 =
+      main_frame1->child_at(0)->current_frame_host();
+  auto* subframe_process =
+      static_cast<RenderProcessHostImpl*>(subframe1->GetProcess());
+  ASSERT_NE(main_frame1->GetProcess(), subframe_process);
+
+  // Ignore private memory footprint updates from the renderer, and pretend
+  // that the subframe process's PMF is currently 5 bytes.
+  RendererHostInterceptor interceptor(subframe_process);
+  subframe_process->SetPrivateMemoryFootprintForTesting(5);
+
+  // Create an unrelated tab and navigate it to a(b).
+  Shell* shell2 = CreateBrowser();
+  EXPECT_TRUE(NavigateToURL(shell2, main_url));
+  RenderFrameHostImpl* main_frame2 =
+      static_cast<WebContentsImpl*>(shell2->web_contents())
+          ->GetPrimaryMainFrame();
+  RenderFrameHostImpl* subframe2 =
+      main_frame2->child_at(0)->current_frame_host();
+  ASSERT_NE(main_frame2->GetProcess(), subframe2->GetProcess());
+
+  // The new b.com subframe should reuse the available b.com process from the
+  // first tab. This is because the process uses 5 bytes of memory, which is
+  // below the reuse threshold of 8 bytes.
+  EXPECT_EQ(subframe2->GetProcess(), subframe_process);
+
+  // Update the subframe process's PMF to 10, pretending that the second
+  // subframe also takes up 5 bytes.
+  subframe_process->SetPrivateMemoryFootprintForTesting(10);
+
+  // Create a third tab and navigate it to a(b).
+  Shell* shell3 = CreateBrowser();
+  EXPECT_TRUE(NavigateToURL(shell3, main_url));
+  RenderFrameHostImpl* main_frame3 =
+      static_cast<WebContentsImpl*>(shell3->web_contents())
+          ->GetPrimaryMainFrame();
+  RenderFrameHostImpl* subframe3 =
+      main_frame3->child_at(0)->current_frame_host();
+  ASSERT_NE(main_frame3->GetProcess(), subframe3->GetProcess());
+
+  // This time, the new b.com subframe should not reuse the available b.com
+  // process from the first two tabs. This is because the process is consuming
+  // 10 bytes of memory, which is above the reuse threshold of 8 bytes.
+  EXPECT_NE(subframe3->GetProcess(), subframe_process);
+
+  // Check that the histogram was recorded when the memory threshold was
+  // exceeded for `subframe_process`. At that time, the process should've had
+  // two total frames.
+  histograms.ExpectTotalCount(
+      "BrowserRenderProcessHost.SubframeProcessReuseThreshold.TotalFrames", 1);
+  histograms.ExpectBucketCount(
+      "BrowserRenderProcessHost.SubframeProcessReuseThreshold.TotalFrames", 2,
+      1);
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          RequestDelayingSitePerProcessBrowserTest,
                          testing::ValuesIn(RenderDocumentFeatureLevelValues()));
@@ -13811,4 +13899,8 @@
                          SitePerProcessWithMainFrameThresholdLocalhostTest,
                          testing::Bool());
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         SitePerProcessWithSubframeProcessReuseThresholdsTest,
+                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index e5f0e79..e10ab15 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -809,15 +809,33 @@
   return is_popup_;
 }
 
+bool WebContentsImpl::IsPartitionedPopin() const {
+  // The feature must be enabled if a popin was opened.
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kPartitionedPopins) ||
+         !partitioned_popin_opener_);
+
+  return !!partitioned_popin_opener_;
+}
+
 RenderFrameHostImpl* WebContentsImpl::PartitionedPopinOpener() const {
   // A popin cannot open a popin so at most one could be set at a time.
   DCHECK(!partitioned_popin_opener_ || !opened_partitioned_popin_);
+
+  // The feature must be enabled if the popin opener is set.
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kPartitionedPopins) ||
+         !partitioned_popin_opener_);
+
   return partitioned_popin_opener_.get();
 }
 
 WebContents* WebContentsImpl::OpenedPartitionedPopin() const {
   // A popin cannot open a popin so at most one could be set at a time.
   DCHECK(!partitioned_popin_opener_ || !opened_partitioned_popin_);
+
+  // The feature must be enabled if a popin was opened.
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kPartitionedPopins) ||
+         !opened_partitioned_popin_);
+
   return opened_partitioned_popin_.get();
 }
 
@@ -11332,6 +11350,11 @@
     WebContentsImpl* new_window,
     const mojom::CreateNewWindowParams& params,
     RenderFrameHostImpl* opener) {
+  // We should not take action if the feature is disabled.
+  if (!base::FeatureList::IsEnabled(blink::features::kPartitionedPopins)) {
+    return;
+  }
+
   // All popins should be counted as popups to ensure proper UX treatment.
   if (!params.features->is_partitioned_popin || !new_window->is_popup_) {
     return;
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 4f90a67..3b50feb7 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1491,6 +1491,8 @@
 
   bool IsPopup() const override;
 
+  bool IsPartitionedPopin() const override;
+
   RenderFrameHostImpl* PartitionedPopinOpener() const override;
 
   WebContents* OpenedPartitionedPopin() const override;
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 096496b..b623473 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -29,6 +29,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_restrictions.h"
@@ -5524,6 +5525,47 @@
   EXPECT_TRUE(browser_td >= renderer_td);
 }
 
+class WebContentsDiscardBrowserTest : public WebContentsImplBrowserTest {
+ public:
+  WebContentsDiscardBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(features::kWebContentsDiscard);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(WebContentsDiscardBrowserTest, DiscardRetainsTitle) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // Navigate to an initial page.
+  const GURL initial_url =
+      embedded_test_server()->GetURL("/frame_tree/top.html");
+  ASSERT_TRUE(NavigateToURL(shell(), initial_url));
+
+  // Update the tab title.
+  const std::u16string test_title(u"test_title");
+  WebContentsImpl* contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  contents->UpdateTitleForEntry(
+      contents->GetController().GetLastCommittedEntry(), test_title);
+  EXPECT_EQ(test_title, contents->GetTitle());
+
+  // Discard the tab.
+  testing::NiceMock<MockWebContentsObserver> observer(contents);
+  EXPECT_CALL(observer, AboutToBeDiscarded(contents)).Times(1);
+  EXPECT_CALL(observer, WasDiscarded()).Times(1);
+  EXPECT_FALSE(contents->WasDiscarded());
+  contents->Discard();
+  EXPECT_TRUE(contents->WasDiscarded());
+  FrameTreeNode* root = contents->GetPrimaryFrameTree().root();
+  ASSERT_TRUE(
+      base::test::RunUntil([&]() { return 0u == root->child_count(); }));
+
+  // The title should remain unchanged post discard.
+  EXPECT_EQ(test_title, contents->GetTitle());
+}
+
 #if !BUILDFLAG(IS_ANDROID)
 class WebContentsImplBrowserTestWindowControlsOverlay
     : public WebContentsImplBrowserTest {
diff --git a/content/browser/xr/service/vr_service_impl.cc b/content/browser/xr/service/vr_service_impl.cc
index 55ecfa1..4b39e69 100644
--- a/content/browser/xr/service/vr_service_impl.cc
+++ b/content/browser/xr/service/vr_service_impl.cc
@@ -46,6 +46,12 @@
 
 namespace {
 
+#if BUILDFLAG(IS_ANDROID)
+constexpr base::TimeDelta kPermissionsDelay = base::Milliseconds(0);
+#else
+constexpr base::TimeDelta kPermissionsDelay = base::Milliseconds(300);
+#endif
+
 device::mojom::XRRuntimeSessionOptionsPtr GetRuntimeOptions(
     device::mojom::XRSessionOptions* options) {
   device::mojom::XRRuntimeSessionOptionsPtr runtime_options =
@@ -565,6 +571,21 @@
   GetPermissionStatus(std::move(request), runtime);
 }
 
+void VRServiceImpl::DoRequestPermissions(
+    const std::vector<blink::PermissionType> request_permissions,
+    base::OnceCallback<void(const std::vector<blink::mojom::PermissionStatus>&)>
+        result_callback) {
+  PermissionController* permission_controller =
+      GetWebContents()->GetBrowserContext()->GetPermissionController();
+  CHECK(permission_controller);
+
+  permission_controller->RequestPermissionsFromCurrentDocument(
+      render_frame_host_,
+      PermissionRequestDescription(request_permissions,
+                                   /*user_gesture=*/true),
+      std::move(result_callback));
+}
+
 void VRServiceImpl::GetPermissionStatus(SessionRequestData request,
                                         BrowserXRRuntimeImpl* runtime) {
   DVLOG(2) << __func__;
@@ -579,19 +600,13 @@
   }
 #endif
 
-  PermissionController* permission_controller =
-      GetWebContents()->GetBrowserContext()->GetPermissionController();
-  DCHECK(permission_controller);
-
   // Need to calculate the permissions before the call below, as otherwise
   // std::move nulls options out before `GetRequiredPermissions()` runs.
   const std::vector<blink::PermissionType> permissions_for_mode =
       GetRequiredPermissionsForMode(request.options->mode);
 
-  permission_controller->RequestPermissionsFromCurrentDocument(
-      render_frame_host_,
-      PermissionRequestDescription(permissions_for_mode,
-                                   /*user_gesture=*/true),
+  DoRequestPermissions(
+      permissions_for_mode,
       base::BindOnce(&VRServiceImpl::OnPermissionResultsForMode,
                      weak_ptr_factory_.GetWeakPtr(), std::move(request),
                      permissions_for_mode));
@@ -627,21 +642,27 @@
     return;
   }
 
-  PermissionController* permission_controller =
-      GetWebContents()->GetBrowserContext()->GetPermissionController();
-  DCHECK(permission_controller);
-
   const std::vector<blink::PermissionType> permissions_for_features =
       GetRequiredPermissionsForFeatures(request.required_features,
                                         request.optional_features);
 
-  permission_controller->RequestPermissionsFromCurrentDocument(
-      render_frame_host_,
-      PermissionRequestDescription(permissions_for_features,
-                                   /* user_gesture = */ true),
+  auto result_callback =
       base::BindOnce(&VRServiceImpl::OnPermissionResultsForFeatures,
                      weak_ptr_factory_.GetWeakPtr(), std::move(request),
-                     permissions_for_features));
+                     permissions_for_features);
+  if (permissions_for_features.empty()) {
+    std::move(result_callback).Run({});
+    return;
+  }
+
+  // TODO(https://crbug.com/364669911): Remove posted task once permissions code
+  // is fixed.
+  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&VRServiceImpl::DoRequestPermissions,
+                     weak_ptr_factory_.GetWeakPtr(), permissions_for_features,
+                     std::move(result_callback)),
+      kPermissionsDelay);
 }
 
 void VRServiceImpl::OnPermissionResultsForFeatures(
diff --git a/content/browser/xr/service/vr_service_impl.h b/content/browser/xr/service/vr_service_impl.h
index 443c5957..9182ac37 100644
--- a/content/browser/xr/service/vr_service_impl.h
+++ b/content/browser/xr/service/vr_service_impl.h
@@ -142,6 +142,11 @@
 
   bool InternalSupportsSession(device::mojom::XRSessionOptions* options);
 
+  void DoRequestPermissions(
+      const std::vector<blink::PermissionType> request_permissions,
+      base::OnceCallback<void(
+          const std::vector<blink::mojom::PermissionStatus>&)> result_callback);
+
   // The following steps are ordered in the general flow for "RequestSession"
   // GetPermissionStatus will result in a call to OnPermissionResult which then
   // calls EnsureRuntimeInstalled (with a callback to OnInstallResult), which
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
index d4a36b07..dc5114d 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
@@ -157,14 +157,6 @@
 
     @Test
     @SmallTest
-    public void test_addAlertWithEmptyText() {
-        performTest(
-                "add-alert-with-empty-text.html",
-                "add-alert-with-empty-text-expected-android.txt");
-    }
-
-    @Test
-    @SmallTest
     public void test_addAlertContent() {
         performTest("add-alert-content.html", "add-alert-content-expected-android.txt");
     }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java
index 235993b..347452fa 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/TextSuggestionMenuTest.java
@@ -97,6 +97,7 @@
 
     @Test
     @LargeTest
+    @DisabledTest(message = "https://crbug.com/1156419")
     public void testDeleteWordMarkedWithSpellingMarker()
             throws InterruptedException, Throwable, TimeoutException {
         WebContents webContents = mRule.getWebContents();
@@ -242,6 +243,7 @@
 
     @Test
     @LargeTest
+    @DisabledTest(message = "https://crbug.com/1156419")
     public void testApplyMisspellingSuggestion()
             throws InterruptedException, Throwable, TimeoutException {
         WebContents webContents = mRule.getWebContents();
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 8a552c5..ede9a34 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1075,6 +1075,23 @@
              "StrictOriginIsolation",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Controls whether subframe process reuse should be restricted according to
+// resource usage policies. Namely, a process that is already consuming too
+// much memory is not attempted to be reused.
+BASE_FEATURE(kSubframeProcessReuseThresholds,
+             "SubframeProcessReuseThresholds",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// Specifies the memory threshold for the `kSubframeProcessReuseThresholds`
+// feature, which only allows a process to be reused for another subframe if the
+// process's memory footprint stays below this threshold. Similar to
+// `kProcessPerSiteMainFrameTotalMemoryLimit`, and only provided as a separate
+// knob so that it can be independently controlled in subframe and main frame
+// process reuse experiments.
+constexpr base::FeatureParam<double> kSubframeProcessReuseMemoryThreshold{
+    &kSubframeProcessReuseThresholds, "SubframeProcessReuseMemoryThreshold",
+    2 * 1024 * 1024 * 1024u};
+
 // Disallows window.{alert, prompt, confirm} if triggered inside a subframe that
 // is not same origin with the main frame.
 BASE_FEATURE(kSuppressDifferentOriginSubframeJSDialogs,
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index ab596a0..a69dcd3 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -241,6 +241,9 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebOTP);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSpareRendererForSitePerProcess);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kStrictOriginIsolation);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kSubframeProcessReuseThresholds);
+CONTENT_EXPORT extern const base::FeatureParam<double>
+    kSubframeProcessReuseMemoryThreshold;
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSuppressDifferentOriginSubframeJSDialogs);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSyntheticPointerActions);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kTouchDragAndContextMenu);
diff --git a/content/renderer/media/win/dcomp_texture_wrapper_impl.cc b/content/renderer/media/win/dcomp_texture_wrapper_impl.cc
index 50e467c3..49aacc8 100644
--- a/content/renderer/media/win/dcomp_texture_wrapper_impl.cc
+++ b/content/renderer/media/win/dcomp_texture_wrapper_impl.cc
@@ -17,15 +17,6 @@
 #include "media/base/media_switches.h"
 #include "media/base/win/mf_helpers.h"
 
-namespace {
-
-// Allow MappableSI to be used for DcompTextureWrapperImpl.
-BASE_FEATURE(kAlwaysUseMappableSIForDcompTextureWrapperImpl,
-             "AlwaysUseMappableSIForDcompTextureWrapperImpl",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-}  // namespace
-
 namespace content {
 
 // A RefCounted wrapper to destroy SharedImage associated with the `mailbox_`.
@@ -255,42 +246,19 @@
                                    gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
                                    gpu::SHARED_IMAGE_USAGE_SCANOUT;
 
-  const bool is_mappable_si_enabled = base::FeatureList::IsEnabled(
-      kAlwaysUseMappableSIForDcompTextureWrapperImpl);
-  std::unique_ptr<gfx::GpuMemoryBuffer> gmb;
-  scoped_refptr<gpu::ClientSharedImage> shared_image;
-
-  if (is_mappable_si_enabled) {
-    shared_image = sii->CreateSharedImage(
-        {viz::SinglePlaneFormat::kBGRA_8888, natural_size, gfx::ColorSpace(),
-         usage, "DCOMPTextureWrapperImpl"},
-        gpu::kNullSurfaceHandle, gfx::BufferUsage::GPU_READ,
-        std::move(dx_handle));
-  } else {
-    gmb = gpu::GpuMemoryBufferImplDXGI::CreateFromHandle(
-        std::move(dx_handle), natural_size, gfx::BufferFormat::BGRA_8888,
-        gfx::BufferUsage::GPU_READ, base::NullCallback(), nullptr, nullptr);
-
-    shared_image = sii->CreateSharedImage(
-        {viz::SinglePlaneFormat::kBGRA_8888, natural_size, gfx::ColorSpace(),
-         usage, "DCOMPTextureWrapperImpl"},
-        gmb->CloneHandle());
-  }
+  auto shared_image = sii->CreateSharedImage(
+      {viz::SinglePlaneFormat::kBGRA_8888, natural_size, gfx::ColorSpace(),
+       usage, "DCOMPTextureWrapperImpl"},
+      gpu::kNullSurfaceHandle, gfx::BufferUsage::GPU_READ,
+      std::move(dx_handle));
   CHECK(shared_image);
+
   gpu::Mailbox mailbox = shared_image->mailbox();
   gpu::SyncToken sync_token = sii->GenVerifiedSyncToken();
 
-  scoped_refptr<media::VideoFrame> video_frame_texture;
-  if (is_mappable_si_enabled) {
-    video_frame_texture = media::VideoFrame::WrapMappableSharedImage(
-        shared_image, sync_token, GL_TEXTURE_2D, base::NullCallback(),
-        gfx::Rect(natural_size), natural_size, base::TimeDelta::Min());
-  } else {
-    video_frame_texture = media::VideoFrame::WrapExternalGpuMemoryBuffer(
-        gfx::Rect(natural_size), natural_size, std::move(gmb), shared_image,
-        sync_token, GL_TEXTURE_2D, base::NullCallback(),
-        base::TimeDelta::Min());
-  }
+  auto video_frame_texture = media::VideoFrame::WrapMappableSharedImage(
+      shared_image, sync_token, GL_TEXTURE_2D, base::NullCallback(),
+      gfx::Rect(natural_size), natural_size, base::TimeDelta::Min());
   video_frame_texture->metadata().wants_promotion_hint = true;
   video_frame_texture->metadata().allow_overlay = true;
 
diff --git a/content/services/auction_worklet/BUILD.gn b/content/services/auction_worklet/BUILD.gn
index 635afd6..84990b1 100644
--- a/content/services/auction_worklet/BUILD.gn
+++ b/content/services/auction_worklet/BUILD.gn
@@ -84,6 +84,8 @@
     "set_priority_signals_override_bindings.h",
     "shared_storage_bindings.cc",
     "shared_storage_bindings.h",
+    "trusted_kvv2_signals.cc",
+    "trusted_kvv2_signals.h",
     "trusted_signals.cc",
     "trusted_signals.h",
     "trusted_signals_kvv2_helper.cc",
@@ -161,6 +163,7 @@
     "public/cpp/auction_downloader_unittest.cc",
     "public/cpp/private_aggregation_reporting_unittest.cc",
     "seller_worklet_unittest.cc",
+    "trusted_kvv2_signals_unittest.cc",
     "trusted_signals_kvv2_helper_unittest.cc",
     "trusted_signals_request_manager_unittest.cc",
     "trusted_signals_unittest.cc",
@@ -190,6 +193,7 @@
     "//content/public/common:common",
     "//gin",
     "//net",
+    "//net:test_support",
     "//services/network:test_support",
     "//services/service_manager/public/cpp/test:test_support",
     "//testing/gmock",
diff --git a/content/services/auction_worklet/trusted_kvv2_signals.cc b/content/services/auction_worklet/trusted_kvv2_signals.cc
new file mode 100644
index 0000000..1345970
--- /dev/null
+++ b/content/services/auction_worklet/trusted_kvv2_signals.cc
@@ -0,0 +1,228 @@
+// 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 "content/services/auction_worklet/trusted_kvv2_signals.h"
+
+#include <algorithm>
+#include <memory>
+#include <optional>
+
+#include "base/check.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
+#include "base/types/expected.h"
+#include "content/services/auction_worklet/auction_v8_helper.h"
+#include "content/services/auction_worklet/public/cpp/auction_network_events_delegate.h"
+#include "content/services/auction_worklet/public/mojom/auction_network_events_handler.mojom.h"
+#include "content/services/auction_worklet/trusted_signals.h"
+#include "content/services/auction_worklet/trusted_signals_kvv2_helper.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "net/http/http_response_headers.h"
+#include "net/third_party/quiche/src/quiche/oblivious_http/buffers/oblivious_http_request.h"
+#include "net/third_party/quiche/src/quiche/oblivious_http/oblivious_http_client.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
+#include "third_party/blink/public/common/features_generated.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+#include "v8/include/v8-context.h"
+
+namespace auction_worklet {
+
+std::unique_ptr<TrustedKVv2Signals> TrustedKVv2Signals::LoadKVv2BiddingSignals(
+    network::mojom::URLLoaderFactory* url_loader_factory,
+    mojo::PendingRemote<auction_worklet::mojom::AuctionNetworkEventsHandler>
+        devtools_pending_remote,
+    std::set<std::string> interest_group_names,
+    std::set<std::string> bidding_signals_keys,
+    const GURL& trusted_bidding_signals_url,
+    std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+        request_helper_builder,
+    scoped_refptr<AuctionV8Helper> v8_helper,
+    LoadKVv2SignalsCallback load_kvv2_signals_callback) {
+  DCHECK(!interest_group_names.empty());
+
+  std::unique_ptr<TrustedSignalsKVv2RequestHelper> request_helper =
+      request_helper_builder->Build();
+
+  std::unique_ptr<TrustedKVv2Signals> trusted_kvv2_signals =
+      base::WrapUnique(new TrustedKVv2Signals(
+          std::move(interest_group_names), std::move(bidding_signals_keys),
+          /*render_urls=*/std::nullopt,
+          /*ad_component_render_urls=*/std::nullopt,
+          trusted_bidding_signals_url,
+          request_helper->TakeOHttpRequestContext(),
+          std::move(devtools_pending_remote), std::move(v8_helper),
+          std::move(load_kvv2_signals_callback)));
+
+  trusted_kvv2_signals->StartKVv2Download(
+      url_loader_factory, trusted_bidding_signals_url,
+      request_helper->TakePostRequestBody());
+
+  return trusted_kvv2_signals;
+}
+
+TrustedKVv2Signals::TrustedKVv2Signals(
+    std::optional<std::set<std::string>> interest_group_names,
+    std::optional<std::set<std::string>> bidding_signals_keys,
+    std::optional<std::set<std::string>> render_urls,
+    std::optional<std::set<std::string>> ad_component_render_urls,
+    const GURL& trusted_signals_url,
+    quiche::ObliviousHttpRequest::Context context,
+    mojo::PendingRemote<auction_worklet::mojom::AuctionNetworkEventsHandler>
+        auction_network_events_handler,
+    scoped_refptr<AuctionV8Helper> v8_helper,
+    LoadKVv2SignalsCallback load_kvv2_signals_callback)
+    : interest_group_names_(std::move(interest_group_names)),
+      bidding_signals_keys_(std::move(bidding_signals_keys)),
+      render_urls_(std::move(render_urls)),
+      ad_component_render_urls_(std::move(ad_component_render_urls)),
+      trusted_signals_url_(trusted_signals_url),
+      v8_helper_(std::move(v8_helper)),
+      load_kvv2_signals_callback_(std::move(load_kvv2_signals_callback)),
+      context_(std::move(context)),
+      auction_network_events_handler_(
+          std::move(auction_network_events_handler)) {
+  DCHECK(v8_helper_);
+  DCHECK(load_kvv2_signals_callback_);
+
+  // Either this should be for bidding signals or scoring signals.
+  DCHECK((interest_group_names_ && bidding_signals_keys_) ||
+         (render_urls_ && ad_component_render_urls_));
+  DCHECK((!interest_group_names_ && !bidding_signals_keys_) ||
+         (!render_urls_ && !ad_component_render_urls_));
+}
+
+TrustedKVv2Signals::~TrustedKVv2Signals() = default;
+
+void TrustedKVv2Signals::StartKVv2Download(
+    network::mojom::URLLoaderFactory* url_loader_factory,
+    const GURL& full_signals_url,
+    std::string post_body) {
+  download_start_time_ = base::TimeTicks::Now();
+
+  std::unique_ptr<MojoNetworkEventsDelegate> network_events_delegate;
+
+  if (auction_network_events_handler_.is_valid()) {
+    network_events_delegate = std::make_unique<MojoNetworkEventsDelegate>(
+        std::move(auction_network_events_handler_));
+  }
+  auction_downloader_ = std::make_unique<AuctionDownloader>(
+      url_loader_factory, full_signals_url,
+      AuctionDownloader::DownloadMode::kActualDownload,
+      AuctionDownloader::MimeType::kAdAuctionTrustedSignals,
+      std::move(post_body),
+      /*content_type=*/kTrustedSignalsKVv2EncryptionRequestMediaType,
+      AuctionDownloader::ResponseStartedCallback(),
+      base::BindOnce(&TrustedKVv2Signals::OnKVv2DownloadComplete,
+                     base::Unretained(this)),
+      /*network_events_delegate=*/std::move(network_events_delegate));
+}
+
+void TrustedKVv2Signals::OnKVv2DownloadComplete(
+    std::unique_ptr<std::string> body,
+    scoped_refptr<net::HttpResponseHeaders> headers,
+    std::optional<std::string> error_msg) {
+  // The downloader's job is done, so clean it up.
+  auction_downloader_.reset();
+
+  // Key-related fields aren't needed after this call, so pass ownership of them
+  // over to the parser on the V8 thread.
+  v8_helper_->v8_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &TrustedKVv2Signals::HandleKVv2DownloadResultOnV8Thread, v8_helper_,
+          trusted_signals_url_, std::move(interest_group_names_),
+          std::move(bidding_signals_keys_), std::move(render_urls_),
+          std::move(ad_component_render_urls_), std::move(body),
+          std::move(headers), std::move(context_), std::move(error_msg),
+          base::SequencedTaskRunner::GetCurrentDefault(),
+          weak_ptr_factory.GetWeakPtr(),
+          base::TimeTicks::Now() - download_start_time_));
+}
+
+// static
+void TrustedKVv2Signals::HandleKVv2DownloadResultOnV8Thread(
+    scoped_refptr<AuctionV8Helper> v8_helper,
+    const GURL& signals_url,
+    std::optional<std::set<std::string>> interest_group_names,
+    std::optional<std::set<std::string>> bidding_signals_keys,
+    std::optional<std::set<std::string>> render_urls,
+    std::optional<std::set<std::string>> ad_component_render_urls,
+    std::unique_ptr<std::string> body,
+    scoped_refptr<net::HttpResponseHeaders> headers,
+    quiche::ObliviousHttpRequest::Context context,
+    std::optional<std::string> error_msg,
+    scoped_refptr<base::SequencedTaskRunner> user_thread_task_runner,
+    base::WeakPtr<TrustedKVv2Signals> weak_instance,
+    base::TimeDelta download_time) {
+  if (!body) {
+    PostKVv2CallbackToUserThread(std::move(user_thread_task_runner),
+                                 weak_instance, /*result_map=*/std::nullopt,
+                                 std::move(error_msg));
+    return;
+  }
+  DCHECK(!error_msg.has_value());
+
+  auto maybe_fetch_result =
+      TrustedSignalsKVv2ResponseParser::ParseResponseToSignalsFetchResult(
+          *body, context);
+  if (!maybe_fetch_result.has_value()) {
+    PostKVv2CallbackToUserThread(
+        std::move(user_thread_task_runner), weak_instance,
+        /*result_map=*/std::nullopt,
+        std::move(maybe_fetch_result).error().error_msg);
+    return;
+  }
+
+  AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper.get());
+  v8::Context::Scope context_scope(v8_helper->scratch_context());
+
+  if (bidding_signals_keys) {
+    CHECK(interest_group_names.has_value());
+    TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMapOrError
+        maybe_result_map = TrustedSignalsKVv2ResponseParser::
+            ParseBiddingSignalsFetchResultToResultMap(
+                v8_helper.get(), interest_group_names.value(),
+                bidding_signals_keys.value(), maybe_fetch_result.value());
+
+    if (!maybe_result_map.has_value()) {
+      PostKVv2CallbackToUserThread(
+          std::move(user_thread_task_runner), weak_instance,
+          /*result_map=*/std::nullopt,
+          std::move(maybe_result_map).error().error_msg);
+      return;
+    }
+
+    PostKVv2CallbackToUserThread(
+        std::move(user_thread_task_runner), weak_instance,
+        std::move(maybe_result_map).value(), std::move(error_msg));
+  }
+}
+
+void TrustedKVv2Signals::PostKVv2CallbackToUserThread(
+    scoped_refptr<base::SequencedTaskRunner> user_thread_task_runner,
+    base::WeakPtr<TrustedKVv2Signals> weak_instance,
+    std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+        result_map,
+    std::optional<std::string> error_msg) {
+  user_thread_task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(&TrustedKVv2Signals::DeliverKVv2CallbackOnUserThread,
+                     weak_instance, std::move(result_map),
+                     std::move(error_msg)));
+}
+
+void TrustedKVv2Signals::DeliverKVv2CallbackOnUserThread(
+    std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+        result_map,
+    std::optional<std::string> error_msg) {
+  std::move(load_kvv2_signals_callback_)
+      .Run(std::move(result_map), std::move(error_msg));
+}
+
+}  // namespace auction_worklet
diff --git a/content/services/auction_worklet/trusted_kvv2_signals.h b/content/services/auction_worklet/trusted_kvv2_signals.h
new file mode 100644
index 0000000..5cfb06f
--- /dev/null
+++ b/content/services/auction_worklet/trusted_kvv2_signals.h
@@ -0,0 +1,158 @@
+// 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 CONTENT_SERVICES_AUCTION_WORKLET_TRUSTED_KVV2_SIGNALS_H_
+#define CONTENT_SERVICES_AUCTION_WORKLET_TRUSTED_KVV2_SIGNALS_H_
+
+#include <map>
+#include <memory>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/functional/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
+#include "base/types/expected.h"
+#include "content/common/content_export.h"
+#include "content/services/auction_worklet/auction_v8_helper.h"
+#include "content/services/auction_worklet/public/mojom/auction_network_events_handler.mojom.h"
+#include "content/services/auction_worklet/trusted_signals.h"
+#include "content/services/auction_worklet/trusted_signals_kvv2_helper.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "net/http/http_response_headers.h"
+#include "net/third_party/quiche/src/quiche/oblivious_http/buffers/oblivious_http_request.h"
+#include "net/third_party/quiche/src/quiche/oblivious_http/oblivious_http_client.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+#include "v8/include/v8-forward.h"
+
+namespace auction_worklet {
+
+class AuctionDownloader;
+
+// Like the TrustedSignals class, but for version 2 of the Key-Value server API
+// for bidding/scoring signals. It fetches and parses the hosted CBOR data
+// needed by worklets. There are separate methods for fetching bidding and
+// scoring signals. A single `TrustedKVv2Signals` object can only be used to
+// fetch either bidding signals or scoring signals, even if a single URL is used
+// for both types of signals.
+class CONTENT_EXPORT TrustedKVv2Signals {
+ public:
+  using LoadKVv2SignalsCallback = base::OnceCallback<void(
+      std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+          result_map,
+      std::optional<std::string> error_msg)>;
+
+  explicit TrustedKVv2Signals(const TrustedKVv2Signals&) = delete;
+  TrustedKVv2Signals& operator=(const TrustedKVv2Signals&) = delete;
+  ~TrustedKVv2Signals();
+
+  // Construct a TrustedKVv2Signals for fetching bidding signals, and start
+  // the fetch. `trusted_bidding_signals_url` must be the base URL (no query
+  // params added). Callback will be invoked asynchronously once the data
+  // has been fetched or an error has occurred.
+  //
+  // There are no lifetime constraints of `url_loader_factory`.
+  static std::unique_ptr<TrustedKVv2Signals> LoadKVv2BiddingSignals(
+      network::mojom::URLLoaderFactory* url_loader_factory,
+      mojo::PendingRemote<auction_worklet::mojom::AuctionNetworkEventsHandler>
+          auction_network_events_handler,
+      std::set<std::string> interest_group_names,
+      std::set<std::string> bidding_signals_keys,
+      const GURL& trusted_bidding_signals_url,
+      std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+          request_helper_builder,
+      scoped_refptr<AuctionV8Helper> v8_helper,
+      LoadKVv2SignalsCallback load_kvv2_signals_callback);
+
+ private:
+  // The `context` is generated during the encryption process in the request
+  // builder and is saved for decrypting the response body.
+  TrustedKVv2Signals(
+      std::optional<std::set<std::string>> interest_group_names,
+      std::optional<std::set<std::string>> bidding_signals_keys,
+      std::optional<std::set<std::string>> render_urls,
+      std::optional<std::set<std::string>> ad_component_render_urls,
+      const GURL& trusted_signals_url,
+      quiche::ObliviousHttpRequest::Context context,
+      mojo::PendingRemote<auction_worklet::mojom::AuctionNetworkEventsHandler>
+          auction_network_events_handler,
+      scoped_refptr<AuctionV8Helper> v8_helper,
+      LoadKVv2SignalsCallback load_kvv2_signals_callback);
+
+  // Start downloading `url`, which should be the bidding or scoring signals
+  // URL, using the POST method..
+  void StartKVv2Download(network::mojom::URLLoaderFactory* url_loader_factory,
+                         const GURL& full_signals_url,
+                         std::string post_body);
+
+  void OnKVv2DownloadComplete(std::unique_ptr<std::string> body,
+                              scoped_refptr<net::HttpResponseHeaders> headers,
+                              std::optional<std::string> error_msg);
+
+  // Parse the response body on the V8 thread, and extract values associated
+  // with the requested keys.
+  static void HandleKVv2DownloadResultOnV8Thread(
+      scoped_refptr<AuctionV8Helper> v8_helper,
+      const GURL& signals_url,
+      std::optional<std::set<std::string>> interest_group_names,
+      std::optional<std::set<std::string>> bidding_signals_keys,
+      std::optional<std::set<std::string>> render_urls,
+      std::optional<std::set<std::string>> ad_component_render_urls,
+      std::unique_ptr<std::string> body,
+      scoped_refptr<net::HttpResponseHeaders> headers,
+      quiche::ObliviousHttpRequest::Context context,
+      std::optional<std::string> error_msg,
+      scoped_refptr<base::SequencedTaskRunner> user_thread_task_runner,
+      base::WeakPtr<TrustedKVv2Signals> weak_instance,
+      base::TimeDelta download_time);
+
+  // Called from V8 thread.
+  static void PostKVv2CallbackToUserThread(
+      scoped_refptr<base::SequencedTaskRunner> user_thread_task_runner,
+      base::WeakPtr<TrustedKVv2Signals> weak_instance,
+      std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+          result_map,
+      std::optional<std::string> error_msg);
+
+  // Called on user thread.
+  void DeliverKVv2CallbackOnUserThread(
+      std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+          result_map,
+      std::optional<std::string> error_msg);
+
+  // Keys being fetched. For bidding signals, only `bidding_signals_keys_` and
+  // `interest_group_names_` are non-null. For scoring signals, only
+  // `render_urls_` and `ad_component_render_urls_` are non-null. These are
+  // cleared and ownership is passed to the V8 thread once the download
+  // completes, as they're no longer on the main thread after that point.
+  std::optional<std::set<std::string>> interest_group_names_;
+  std::optional<std::set<std::string>> bidding_signals_keys_;
+  std::optional<std::set<std::string>> render_urls_;
+  std::optional<std::set<std::string>> ad_component_render_urls_;
+
+  const GURL trusted_signals_url_;
+  const scoped_refptr<AuctionV8Helper> v8_helper_;
+
+  LoadKVv2SignalsCallback load_kvv2_signals_callback_;
+  std::unique_ptr<AuctionDownloader> auction_downloader_;
+  // Used only for metrics; time when download started.
+  base::TimeTicks download_start_time_;
+  // Save the encryption context for decryption purpose.
+  quiche::ObliviousHttpRequest::Context context_;
+
+  mojo::PendingRemote<auction_worklet::mojom::AuctionNetworkEventsHandler>
+      auction_network_events_handler_;
+
+  base::WeakPtrFactory<TrustedKVv2Signals> weak_ptr_factory{this};
+};
+
+}  // namespace auction_worklet
+
+#endif  // CONTENT_SERVICES_AUCTION_WORKLET_TRUSTED_KVV2_SIGNALS_H_
diff --git a/content/services/auction_worklet/trusted_kvv2_signals_unittest.cc b/content/services/auction_worklet/trusted_kvv2_signals_unittest.cc
new file mode 100644
index 0000000..528b823
--- /dev/null
+++ b/content/services/auction_worklet/trusted_kvv2_signals_unittest.cc
@@ -0,0 +1,545 @@
+// 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 "content/services/auction_worklet/trusted_kvv2_signals.h"
+
+#include <cstddef>
+
+#include "base/containers/span_writer.h"
+#include "base/feature_list.h"
+#include "base/functional/bind.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "components/cbor/values.h"
+#include "components/cbor/writer.h"
+#include "content/common/features.h"
+#include "content/services/auction_worklet/auction_v8_helper.h"
+#include "content/services/auction_worklet/trusted_signals_kvv2_helper.h"
+#include "content/services/auction_worklet/worklet_test_util.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/third_party/quiche/src/quiche/oblivious_http/oblivious_http_gateway.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/features_generated.h"
+#include "third_party/zlib/google/compression_utils.h"
+#include "url/gurl.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-forward.h"
+
+namespace auction_worklet {
+namespace {
+
+const char kPublisherHostName[] = "publisher.test";
+const int kExperimentGroupId = 12345;
+const char kTrustedBiddingSignalsSlotSizeParam[] = "slotSize=100,200";
+const char kJoiningOrigin[] = "https://foo.test/";
+const size_t kFramingHeaderSize = 5;  // bytes
+const size_t kOhttpHeaderSize = 55;   // bytes
+const char kTrustedBiddingSignalsPath[] = "/bidder-signals";
+const char kTrustedSignalsHost[] = "a.test";
+const uint8_t kKeyId = 0xFF;
+
+// These keys were randomly generated as follows:
+// EVP_HPKE_KEY keys;
+// EVP_HPKE_KEY_generate(&keys, EVP_hpke_x25519_hkdf_sha256());
+// and then EVP_HPKE_KEY_public_key and EVP_HPKE_KEY_private_key were used to
+// extract the keys.
+const uint8_t kTestPrivateKey[] = {
+    0xff, 0x1f, 0x47, 0xb1, 0x68, 0xb6, 0xb9, 0xea, 0x65, 0xf7, 0x97,
+    0x4f, 0xf2, 0x2e, 0xf2, 0x36, 0x94, 0xe2, 0xf6, 0xb6, 0x8d, 0x66,
+    0xf3, 0xa7, 0x64, 0x14, 0x28, 0xd4, 0x45, 0x35, 0x01, 0x8f,
+};
+
+const uint8_t kTestPublicKey[] = {
+    0xa1, 0x5f, 0x40, 0x65, 0x86, 0xfa, 0xc4, 0x7b, 0x99, 0x59, 0x70,
+    0xf1, 0x85, 0xd9, 0xd8, 0x91, 0xc7, 0x4d, 0xcf, 0x1e, 0xb9, 0x1a,
+    0x7d, 0x50, 0xa5, 0x8b, 0x01, 0x68, 0x3e, 0x60, 0x05, 0x2d,
+};
+
+// Hex string for bidding signals base response, converted from the following
+// CBOR data:
+// [
+//   {
+//     "id": 0,
+//     "keyGroupOutputs": [
+//       {
+//         "tags": [
+//           "interestGroupNames"
+//         ],
+//         "keyValues": {
+//           "name1": {
+//             "value":
+//             "{\"priorityVector\":{\"signal1\":1},
+//               \"updateIfOlderThanMs\":3600000}"
+//           }
+//         }
+//       },
+//       {
+//         "tags": [
+//           "keys"
+//         ],
+//         "keyValues": {
+//           "key1": {
+//             "value": "\"value1\""
+//           }
+//         }
+//       }
+//     ]
+//   }
+// ]
+const char kBiddingContentBase[] =
+    "81A2626964006F6B657947726F75704F75747075747382A264746167738172696E74657265"
+    "737447726F75704E616D6573696B657956616C756573A1656E616D6531A16576616C756578"
+    "3F7B227072696F72697479566563746F72223A7B227369676E616C31223A317D2C22757064"
+    "61746549664F6C6465725468616E4D73223A20333630303030307DA2647461677381646B65"
+    "7973696B657956616C756573A1646B657931A16576616C7565682276616C75653122";
+
+std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+CreateRequestHelperBuilder() {
+  // Create a public key.
+  mojom::TrustedSignalsPublicKeyPtr public_key =
+      mojom::TrustedSignalsPublicKey::New(
+          std::string(reinterpret_cast<const char*>(&kTestPublicKey[0]),
+                      sizeof(kTestPublicKey)),
+          kKeyId);
+
+  return std::make_unique<TrustedBiddingSignalsKVv2RequestHelperBuilder>(
+      kPublisherHostName, kExperimentGroupId, std::move(public_key),
+      kTrustedBiddingSignalsSlotSizeParam);
+}
+
+class TrustedKVv2SignalsEmbeddedTest : public testing::Test {
+ public:
+  TrustedKVv2SignalsEmbeddedTest() {
+    SetContentHex(kBiddingContentBase);
+    SetResponseStatusCode(net::HttpStatusCode::HTTP_OK);
+
+    feature_list_.InitWithFeatures(
+        {features::kInterestGroupUpdateIfOlderThan,
+         blink::features::kFledgeTrustedSignalsKVv2Support},
+        {});
+    embedded_test_server_.SetSSLConfig(
+        net::EmbeddedTestServer::CERT_TEST_NAMES);
+    embedded_test_server_.RegisterRequestHandler(base::BindRepeating(
+        &TrustedKVv2SignalsEmbeddedTest::HandleSignalsRequest,
+        base::Unretained(this)));
+    EXPECT_TRUE(embedded_test_server_.Start());
+  }
+
+  ~TrustedKVv2SignalsEmbeddedTest() override {
+    base::AutoLock auto_lock(lock_);
+    // Wait until idle to ensure all requests have been observed within the
+    // `auction_network_events_handler_`.
+    task_environment_.RunUntilIdle();
+  }
+
+  GURL TrustedBiddingSignalsUrl() const {
+    return embedded_test_server_.GetURL(kTrustedSignalsHost,
+                                        kTrustedBiddingSignalsPath);
+  }
+
+  // Fetch bidding signals and wait for completion. Return nullptr on
+  // failure.
+  std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+  FetchBiddingSignals(
+      std::set<std::string> interest_group_names,
+      std::set<std::string> trusted_bidding_signals_keys,
+      std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+          request_helper_builder) {
+    CHECK(!load_signals_run_loop_);
+    DCHECK(!load_kvv2_signals_result_);
+
+    auto bidding_signals = TrustedKVv2Signals::LoadKVv2BiddingSignals(
+        url_loader_factory_.get(),
+        auction_network_events_handler_.CreateRemote(),
+        std::move(interest_group_names),
+        std::move(trusted_bidding_signals_keys), TrustedBiddingSignalsUrl(),
+        std::move(request_helper_builder), v8_helper_,
+        base::BindOnce(&TrustedKVv2SignalsEmbeddedTest::LoadKVv2SignalsCallback,
+                       base::Unretained(this)));
+    WaitForLoadComplete();
+    return std::move(load_kvv2_signals_result_);
+  }
+
+  // Wait for LoadKVv2SignalsCallback to be invoked.
+  void WaitForLoadComplete() {
+    // Since LoadKVv2SignalsCallback is always invoked asynchronously, fine to
+    // create the RunLoop after creating the TrustedKVv2Signals object, which
+    // will ultimately trigger the invocation.
+    load_signals_run_loop_ = std::make_unique<base::RunLoop>();
+    load_signals_run_loop_->Run();
+    load_signals_run_loop_.reset();
+  }
+
+  // Return the result of calling TrustedSignals::Result::GetBiddingSignals()
+  // with `index` and `trusted_bidding_signals_keys`. Return value as a JSON
+  // std::string, for easy testing.
+  std::string ExtractBiddingSignals(
+      TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap& result_map,
+      TrustedSignalsKVv2RequestHelperBuilder::IsolationIndex& index,
+      std::vector<std::string> trusted_bidding_signals_keys) {
+    EXPECT_TRUE(result_map.contains(index));
+    base::RunLoop run_loop;
+
+    std::string result;
+    v8_helper_->v8_runner()->PostTask(
+        FROM_HERE, base::BindLambdaForTesting([&]() {
+          AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper_.get());
+          v8::Isolate* isolate = v8_helper_->isolate();
+          // Could use the scratch context, but using a separate one more
+          // closely resembles actual use.
+          v8::Local<v8::Context> context = v8::Context::New(isolate);
+          v8::Context::Scope context_scope(context);
+
+          v8::Local<v8::Value> value = result_map.at(index)->GetBiddingSignals(
+              v8_helper_.get(), context, trusted_bidding_signals_keys);
+
+          if (v8_helper_->ExtractJson(context, value,
+                                      /*script_timeout=*/nullptr, &result) !=
+              AuctionV8Helper::Result::kSuccess) {
+            result = "JSON extraction failed.";
+          }
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+    return result;
+  }
+
+  // Set the response status code.
+  void SetResponseStatusCode(net::HttpStatusCode code) {
+    base::AutoLock auto_lock(lock_);
+    response_status_code_ = code;
+  }
+
+  // Set the content hex.
+  void SetContentHex(std::string hex) {
+    base::AutoLock auto_lock(lock_);
+    content_hex_ = std::move(hex);
+  }
+
+  // Set the response body hex.
+  void SetResponseBodyHex(std::string hex) {
+    base::AutoLock auto_lock(lock_);
+    response_body_hex_ = std::move(hex);
+  }
+
+ protected:
+  static std::string BuildResponseBody(const std::string& hex_string) {
+    std::vector<uint8_t> hex_bytes;
+    base::HexStringToBytes(hex_string, &hex_bytes);
+
+    std::string response_body;
+    size_t size_before_padding =
+        kFramingHeaderSize + kOhttpHeaderSize + hex_bytes.size();
+    size_t desired_size = absl::bit_ceil(size_before_padding);
+    size_t response_body_size = desired_size - kOhttpHeaderSize;
+    response_body.resize(response_body_size, 0x00);
+
+    base::SpanWriter writer(
+        base::as_writable_bytes(base::make_span(response_body)));
+    writer.WriteU8BigEndian(0x00);
+    writer.WriteU32BigEndian(hex_bytes.size());
+    writer.Write(base::as_bytes(base::make_span(hex_bytes)));
+
+    return response_body;
+  }
+
+  std::unique_ptr<net::test_server::HttpResponse> HandleSignalsRequest(
+      const net::test_server::HttpRequest& request) {
+    base::AutoLock auto_lock(lock_);
+    if (request.relative_url != kTrustedBiddingSignalsPath) {
+      return nullptr;
+    }
+
+    if (!request.headers.contains(net::HttpRequestHeaders::kContentType) ||
+        request.headers.at(net::HttpRequestHeaders::kContentType) !=
+            kTrustedSignalsKVv2EncryptionRequestMediaType) {
+      return nullptr;
+    }
+
+    std::vector<uint8_t> compression_group_bytes;
+    base::HexStringToBytes(content_hex_, &compression_group_bytes);
+
+    cbor::Value::MapValue compression_group;
+    compression_group.try_emplace(cbor::Value("compressionGroupId"),
+                                  cbor::Value(0));
+    compression_group.try_emplace(cbor::Value("ttlMs"), cbor::Value(100));
+    compression_group.try_emplace(cbor::Value("content"),
+                                  cbor::Value(compression_group_bytes));
+
+    cbor::Value::ArrayValue compression_groups;
+    compression_groups.emplace_back(std::move(compression_group));
+
+    cbor::Value::MapValue body_map;
+    body_map.try_emplace(cbor::Value("compressionGroups"),
+                         cbor::Value(std::move(compression_groups)));
+
+    cbor::Value body_value(std::move(body_map));
+    std::optional<std::vector<uint8_t>> maybe_body_bytes =
+        cbor::Writer::Write(body_value);
+
+    std::string response_body = response_body_hex_.empty()
+                                    ? BuildResponseBody(base::HexEncode(
+                                          std::move(maybe_body_bytes).value()))
+                                    : BuildResponseBody(response_body_hex_);
+
+    auto response_key_config = quiche::ObliviousHttpHeaderKeyConfig::Create(
+        kKeyId, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
+        EVP_HPKE_AES_256_GCM);
+    CHECK(response_key_config.ok()) << response_key_config.status();
+
+    auto ohttp_gateway =
+        quiche::ObliviousHttpGateway::Create(
+            std::string(reinterpret_cast<const char*>(&kTestPrivateKey[0]),
+                        sizeof(kTestPrivateKey)),
+            response_key_config.value())
+            .value();
+    auto received_request = ohttp_gateway.DecryptObliviousHttpRequest(
+        request.content, kTrustedSignalsKVv2EncryptionRequestMediaType);
+    CHECK(received_request.ok()) << received_request.status();
+
+    auto response_context =
+        std::move(received_request).value().ReleaseContext();
+
+    // Encrypt the response body.
+    auto maybe_response = ohttp_gateway.CreateObliviousHttpResponse(
+        response_body, response_context,
+        kTrustedSignalsKVv2EncryptionResponseMediaType);
+    CHECK(maybe_response.ok()) << maybe_response.status();
+
+    auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+    response->set_content_type(kAdAuctionTrustedSignalsMimeType);
+    response->set_content(maybe_response->EncapsulateAndSerialize());
+    response->set_code(response_status_code_);
+    response->AddCustomHeader("Ad-Auction-Allowed", "true");
+
+    return response;
+  }
+
+  void LoadKVv2SignalsCallback(
+      std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+          result_map,
+      std::optional<std::string> error_msg) {
+    DCHECK(!load_kvv2_signals_result_);
+    load_kvv2_signals_result_ = std::move(result_map);
+    error_msg_ = std::move(error_msg);
+    if (!expect_nonfatal_error_) {
+      EXPECT_EQ(!load_kvv2_signals_result_.has_value(), error_msg_.has_value());
+    } else {
+      EXPECT_TRUE(load_kvv2_signals_result_);
+      EXPECT_TRUE(error_msg_);
+    }
+    load_signals_run_loop_->Quit();
+  }
+
+  // Need to use an IO thread for the TestSharedURLLoaderFactory, which lives on
+  // the thread it's created on, to make network requests.
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::IO};
+
+  // Reusable run loop for loading the signals. It's always populated after
+  // creating the worklet, to cause a crash if the callback is invoked
+  // synchronously.
+  std::unique_ptr<base::RunLoop> load_signals_run_loop_;
+  std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+      load_kvv2_signals_result_;
+  std::optional<std::string> error_msg_;
+
+  // If false, only one of `result_map` or `error_msg` is expected to be
+  // received in LoadSignalsCallback().
+  bool expect_nonfatal_error_ = false;
+
+  TestAuctionNetworkEventsHandler auction_network_events_handler_;
+  scoped_refptr<AuctionV8Helper> v8_helper_{
+      AuctionV8Helper::Create(AuctionV8Helper::CreateTaskRunner())};
+  base::test::ScopedFeatureList feature_list_;
+
+  base::Lock lock_;
+  net::HttpStatusCode response_status_code_ GUARDED_BY(lock_);
+  std::string content_hex_ GUARDED_BY(lock_);
+  std::string response_body_hex_ GUARDED_BY(lock_);
+
+  net::test_server::EmbeddedTestServer embedded_test_server_{
+      net::test_server::EmbeddedTestServer::TYPE_HTTPS};
+
+  // URLLoaderFactory that makes real network requests.
+  scoped_refptr<network::TestSharedURLLoaderFactory> url_loader_factory_{
+      base::MakeRefCounted<network::TestSharedURLLoaderFactory>(
+          /*network_service=*/nullptr,
+          /*is_trusted=*/true)};
+};
+
+TEST_F(TrustedKVv2SignalsEmbeddedTest, BiddingSignalsBaseResponse) {
+  std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+      request_helper_builder = CreateRequestHelperBuilder();
+
+  request_helper_builder->AddTrustedSignalsRequest(
+      std::string("name1"), std::set<std::string>{"key1"},
+      url::Origin::Create(GURL(kJoiningOrigin)),
+      blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode);
+
+  std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+      result_map = FetchBiddingSignals({"name1"}, {"key1"},
+                                       std::move(request_helper_builder));
+  ASSERT_TRUE(result_map);
+  TrustedSignalsKVv2RequestHelperBuilder::IsolationIndex index(0, 0);
+  EXPECT_EQ(R"({"key1":"value1"})",
+            ExtractBiddingSignals(result_map.value(), index, {"key1"}));
+  const TrustedSignals::Result::PerGroupData* name1_per_group_data =
+      result_map->at(index)->GetPerGroupData("name1");
+  ASSERT_NE(name1_per_group_data, nullptr);
+  auto priority_vector = name1_per_group_data->priority_vector;
+  ASSERT_TRUE(priority_vector);
+  EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"signal1", 1}}),
+            *priority_vector);
+  EXPECT_EQ(base::Milliseconds(3600000),
+            name1_per_group_data->update_if_older_than);
+
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(TrustedKVv2SignalsEmbeddedTest,
+       BiddingSignalsExpectedEntriesNotPresent) {
+  std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+      request_helper_builder = CreateRequestHelperBuilder();
+
+  request_helper_builder->AddTrustedSignalsRequest(
+      std::string("name2"), std::set<std::string>{"key2"},
+      url::Origin::Create(GURL(kJoiningOrigin)),
+      blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode);
+
+  std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
+      result_map = FetchBiddingSignals({"name2"}, {"key2"},
+                                       std::move(request_helper_builder));
+  ASSERT_TRUE(result_map);
+  TrustedSignalsKVv2RequestHelperBuilder::IsolationIndex index(0, 0);
+  EXPECT_EQ(R"({"key2":null})",
+            ExtractBiddingSignals(result_map.value(), index, {"key2"}));
+  EXPECT_EQ(nullptr, result_map->at(index)->GetPerGroupData("name2"));
+}
+
+TEST_F(TrustedKVv2SignalsEmbeddedTest, BiddingSignalsNetworkError) {
+  SetResponseStatusCode(net::HttpStatusCode::HTTP_NOT_FOUND);
+
+  std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+      request_helper_builder = CreateRequestHelperBuilder();
+  request_helper_builder->AddTrustedSignalsRequest(
+      std::string("name1"), std::set<std::string>{"key1"},
+      url::Origin::Create(GURL(kJoiningOrigin)),
+      blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode);
+
+  EXPECT_FALSE(FetchBiddingSignals({"name1"}, {"key1"},
+                                   std::move(request_helper_builder)));
+  ASSERT_TRUE(error_msg_.has_value());
+  EXPECT_EQ(error_msg_.value(),
+            base::StringPrintf("Failed to load %s HTTP status = 404 Not Found.",
+                               TrustedBiddingSignalsUrl().spec().c_str()));
+
+  // Wait until idle to ensure all requests have been observed within the
+  // `auction_network_events_handler_`.
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(TrustedKVv2SignalsEmbeddedTest, FailedToParseResponseBody) {
+  // Random 20 bytes hex string which cannot be parsed as CBOR.
+  SetResponseBodyHex("666f421a72ed47aade0c63826288d5d1bbf2dc2a");
+
+  std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+      request_helper_builder = CreateRequestHelperBuilder();
+  request_helper_builder->AddTrustedSignalsRequest(
+      std::string("name1"), std::set<std::string>{"key1"},
+      url::Origin::Create(GURL(kJoiningOrigin)),
+      blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode);
+
+  EXPECT_FALSE(FetchBiddingSignals({"name1"}, {"key1"},
+                                   std::move(request_helper_builder)));
+  ASSERT_TRUE(error_msg_.has_value());
+  EXPECT_EQ(error_msg_.value(), "Failed to parse response body as CBOR.");
+
+  // Wait until idle to ensure all requests have been observed within the
+  // `auction_network_events_handler_`.
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(TrustedKVv2SignalsEmbeddedTest, FailedToParseSignalsFetchResult) {
+  // Random 20 bytes hex string which cannot be parsed as CBOR.
+  SetContentHex("666f421a72ed47aade0c63826288d5d1bbf2dc2a");
+
+  std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+      request_helper_builder = CreateRequestHelperBuilder();
+  request_helper_builder->AddTrustedSignalsRequest(
+      std::string("name1"), std::set<std::string>{"key1"},
+      url::Origin::Create(GURL(kJoiningOrigin)),
+      blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode);
+
+  EXPECT_FALSE(FetchBiddingSignals({"name1"}, {"key1"},
+                                   std::move(request_helper_builder)));
+  ASSERT_TRUE(error_msg_.has_value());
+  EXPECT_EQ(error_msg_.value(), "Failed to parse content as CBOR.");
+
+  // Wait until idle to ensure all requests have been observed within the
+  // `auction_network_events_handler_`.
+  task_environment_.RunUntilIdle();
+}
+
+class TrustedKVv2SignalsTest : public testing::Test {
+ public:
+  TrustedKVv2SignalsTest() {
+    feature_list_.InitAndEnableFeature(
+        blink::features::kFledgeTrustedSignalsKVv2Support);
+  }
+
+  ~TrustedKVv2SignalsTest() override { task_environment_.RunUntilIdle(); }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  TestAuctionNetworkEventsHandler auction_network_events_handler_;
+  network::TestURLLoaderFactory url_loader_factory_;
+  scoped_refptr<AuctionV8Helper> v8_helper_{
+      AuctionV8Helper::Create(AuctionV8Helper::CreateTaskRunner())};
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Test case where the loader is deleted after it queued the parsing of
+// the script on V8 thread, but before it gets to finish.
+TEST_F(TrustedKVv2SignalsTest, BiddingSignalsDeleteBeforeCallback) {
+  GURL url = GURL("https://url.test/");
+  std::string headers =
+      base::StringPrintf("%s\nContent-Type: %s", kAllowFledgeHeader,
+                         "message/ad-auction-trusted-signals-request");
+  // Parsing process is occurring on the V8 thread, so a non-CBOR response body
+  // will not cause any issue.
+  AddResponse(&url_loader_factory_, url, kAdAuctionTrustedSignalsMimeType,
+              /*charset=*/std::nullopt, "Fake response body", headers);
+
+  // Wedge the V8 thread to control when the JSON parsing takes place.
+  base::WaitableEvent* event_handle = WedgeV8Thread(v8_helper_.get());
+
+  std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
+      request_helper_builder = CreateRequestHelperBuilder();
+  auto bidding_signals = TrustedKVv2Signals::LoadKVv2BiddingSignals(
+      &url_loader_factory_, auction_network_events_handler_.CreateRemote(),
+      {"name1"}, {"key1"}, url, std::move(request_helper_builder), v8_helper_,
+      base::BindOnce([](std::optional<TrustedSignalsKVv2ResponseParser::
+                                          TrustedSignalsResultMap> result,
+                        std::optional<std::string> error_msg) {
+        ADD_FAILURE() << "Callback should not be invoked since loader deleted";
+      }));
+  base::RunLoop().RunUntilIdle();
+  bidding_signals.reset();
+  event_handle->Signal();
+}
+
+}  // namespace
+}  // namespace auction_worklet
diff --git a/content/services/auction_worklet/trusted_signals_kvv2_helper.h b/content/services/auction_worklet/trusted_signals_kvv2_helper.h
index 54db65cc..d472ad51 100644
--- a/content/services/auction_worklet/trusted_signals_kvv2_helper.h
+++ b/content/services/auction_worklet/trusted_signals_kvv2_helper.h
@@ -35,13 +35,11 @@
 
 namespace auction_worklet {
 
-inline constexpr std::string_view
-    kTrustedSignalsKVv2EncryptionRequestMediaType =
-        "message/ad-auction-trusted-signals-request";
+inline constexpr char kTrustedSignalsKVv2EncryptionRequestMediaType[] =
+    "message/ad-auction-trusted-signals-request";
 
-inline constexpr std::string_view
-    kTrustedSignalsKVv2EncryptionResponseMediaType =
-        "message/ad-auction-trusted-signals-response";
+inline constexpr char kTrustedSignalsKVv2EncryptionResponseMediaType[] =
+    "message/ad-auction-trusted-signals-response";
 
 class CONTENT_EXPORT TrustedSignalsKVv2RequestHelper {
  public:
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist
index 58b4b47..6fa361a 100644
--- a/content/test/content_test_bundle_data.filelist
+++ b/content/test/content_test_bundle_data.filelist
@@ -2088,12 +2088,6 @@
 data/accessibility/event/add-alert-expected-mac.txt
 data/accessibility/event/add-alert-expected-uia-win.txt
 data/accessibility/event/add-alert-expected-win.txt
-data/accessibility/event/add-alert-with-empty-text-expected-android.txt
-data/accessibility/event/add-alert-with-empty-text-expected-auralinux.txt
-data/accessibility/event/add-alert-with-empty-text-expected-mac.txt
-data/accessibility/event/add-alert-with-empty-text-expected-uia-win.txt
-data/accessibility/event/add-alert-with-empty-text-expected-win.txt
-data/accessibility/event/add-alert-with-empty-text.html
 data/accessibility/event/add-alert-with-role-change-expected-android.txt
 data/accessibility/event/add-alert-with-role-change-expected-auralinux.txt
 data/accessibility/event/add-alert-with-role-change-expected-mac.txt
diff --git a/content/test/data/accessibility/event/add-alert-expected-uia-win.txt b/content/test/data/accessibility/event/add-alert-expected-uia-win.txt
index 40b1c89..a2651c7 100644
--- a/content/test/data/accessibility/event/add-alert-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/add-alert-expected-uia-win.txt
@@ -1,3 +1,3 @@
-SystemAlert on role=alert, name=a
-SystemAlert on role=alert, name=b
-SystemAlert on role=alert, name=c
+LiveRegionChanged on role=alert, name=a
+LiveRegionChanged on role=alert, name=b
+LiveRegionChanged on role=alert, name=c
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-android.txt b/content/test/data/accessibility/event/add-alert-with-empty-text-expected-android.txt
deleted file mode 100644
index b58892ac..0000000
--- a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-android.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-TYPE_ANNOUNCEMENT - [Hello world!!]
-TYPE_ANNOUNCEMENT - [Hello world!!]
-TYPE_ANNOUNCEMENT - [First alert]
diff --git a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-auralinux.txt b/content/test/data/accessibility/event/add-alert-with-empty-text-expected-auralinux.txt
deleted file mode 100644
index 3ca8c55..0000000
--- a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-auralinux.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_STATIC) role=ROLE_NOTIFICATION ENABLED,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED:ADD index:3 CHILD:(role=ROLE_NOTIFICATION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED:ADD index:3 CHILD:(role=ROLE_NOTIFICATION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED:ADD index:4 CHILD:(role=ROLE_NOTIFICATION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED:REMOVE index:3 CHILD:(role=ROLE_NOTIFICATION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
-CHILDREN-CHANGED:REMOVE index:4 CHILD:(role=ROLE_NOTIFICATION) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
-TEXT-INSERT (start=0 length=11 'First alert') role=ROLE_NOTIFICATION name='(null)' ENABLED,SENSITIVE,SHOWING,VISIBLE
-TEXT-INSERT (start=0 length=13 'Hello world!!') role=ROLE_NOTIFICATION name='(null)' ENABLED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-mac.txt b/content/test/data/accessibility/event/add-alert-with-empty-text-expected-mac.txt
deleted file mode 100644
index d4642b9..0000000
--- a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-mac.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-AXLiveRegionChanged on AXGroup AXSubrole=AXApplicationAlert
-AXLiveRegionChanged on AXGroup AXSubrole=AXApplicationAlert
-AXLiveRegionChanged on AXGroup AXSubrole=AXApplicationAlert
-AXLiveRegionCreated on AXGroup AXSubrole=AXApplicationAlert
-AXLiveRegionCreated on AXGroup AXSubrole=AXApplicationAlert
diff --git a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-uia-win.txt b/content/test/data/accessibility/event/add-alert-with-empty-text-expected-uia-win.txt
deleted file mode 100644
index 7f888fe..0000000
--- a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-uia-win.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-LiveRegionChanged on role=alert
-SystemAlert on role=alert
diff --git a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-win.txt b/content/test/data/accessibility/event/add-alert-with-empty-text-expected-win.txt
deleted file mode 100644
index 70b637ff..0000000
--- a/content/test/data/accessibility/event/add-alert-with-empty-text-expected-win.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-EVENT_OBJECT_LIVEREGIONCHANGED on <div#foo> role=ROLE_SYSTEM_ALERT
-EVENT_OBJECT_SHOW on <div#hidden_alert_non_empty_text> role=ROLE_SYSTEM_ALERT
-EVENT_OBJECT_SHOW on role=ROLE_SYSTEM_STATICTEXT name="First alert"
-EVENT_SYSTEM_ALERT on <div#hidden_alert_non_empty_text> role=ROLE_SYSTEM_ALERT
-IA2_EVENT_TEXT_INSERTED on <#document> role=ROLE_SYSTEM_DOCUMENT value~=[doc-url] FOCUSED,FOCUSABLE new_text={'<obj><obj>' start=3 end=5}
-IA2_EVENT_TEXT_INSERTED on <div#foo> role=ROLE_SYSTEM_ALERT new_text={'First alert' start=0 end=11}
diff --git a/content/test/data/accessibility/event/add-alert-with-empty-text.html b/content/test/data/accessibility/event/add-alert-with-empty-text.html
deleted file mode 100644
index cd6ae5c..0000000
--- a/content/test/data/accessibility/event/add-alert-with-empty-text.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-@WIN-DENY:EVENT_OBJECT_LOCATIONCHANGE*
-@WIN-DENY:EVENT_OBJECT_REORDER*
-@UIA-WIN-DENY:StructureChanged/*
--->
-<!DOCTYPE html>
-<html>
-<body>
-<div id="foo" role="alert"></div>
-<div id="bar" role="alert"></div>
-<div role="alert">
-  <p id="baz"></p>
-</div>
-<div id="hidden_alert_non_empty_text" style="display: none;" role="alert">
-  Hello world!!
-</div>
-<div id="hidden_alert_empty_text" style="display: none;" role="alert"></div>
-<script>
-  function go() {
-    // Should fire an alert event.
-    document.getElementById("foo").innerHTML = "First alert";
-    // Shouldn't fire an alert event.
-    document.getElementById("bar").innerHTML = "";
-    // Shouldn't fire an alert event.
-    document.getElementById("baz").innerHTML = "     ";
-    // Should fire an alert event.
-    document.getElementById("hidden_alert_non_empty_text").style.display = "block";
-    // Shouldn't fire an alert event.
-    document.getElementById("hidden_alert_empty_text").style.display = "block";
-  }
-</script>
-</body>
-</html>
diff --git a/content/test/data/accessibility/event/add-alert-with-role-change-expected-uia-win.txt b/content/test/data/accessibility/event/add-alert-with-role-change-expected-uia-win.txt
index 18257334..cb12386a 100644
--- a/content/test/data/accessibility/event/add-alert-with-role-change-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/add-alert-with-role-change-expected-uia-win.txt
@@ -1 +1 @@
-SystemAlert on role=alert
\ No newline at end of file
+LiveRegionChanged on role=alert
diff --git a/content/test/data/accessibility/event/add-alert-with-role-change.html b/content/test/data/accessibility/event/add-alert-with-role-change.html
index 67eb622e..25f1ae0 100644
--- a/content/test/data/accessibility/event/add-alert-with-role-change.html
+++ b/content/test/data/accessibility/event/add-alert-with-role-change.html
@@ -3,6 +3,7 @@
 @WIN-ALLOW:EVENT_SYSTEM_ALERT*
 @UIA-WIN-DENY:*
 @UIA-WIN-ALLOW:SystemAlert*
+@UIA-WIN-ALLOW:LiveRegion*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/event/add-alert.html b/content/test/data/accessibility/event/add-alert.html
index 931219d..bd22e55b 100644
--- a/content/test/data/accessibility/event/add-alert.html
+++ b/content/test/data/accessibility/event/add-alert.html
@@ -3,6 +3,7 @@
 @WIN-ALLOW:EVENT_SYSTEM_ALERT*
 @UIA-WIN-DENY:*
 @UIA-WIN-ALLOW:SystemAlert*
+@UIA-WIN-ALLOW:LiveRegionChanged*
 -->
 <!DOCTYPE html>
 <html>
diff --git a/content/test/data/accessibility/event/report-validity-invalid-field-expected-win.txt b/content/test/data/accessibility/event/report-validity-invalid-field-expected-win.txt
index 62ad493..55c664b 100644
--- a/content/test/data/accessibility/event/report-validity-invalid-field-expected-win.txt
+++ b/content/test/data/accessibility/event/report-validity-invalid-field-expected-win.txt
@@ -1 +1,2 @@
 EVENT_OBJECT_FOCUS on <input#in1> role=ROLE_SYSTEM_TEXT name="Pet name:" FOCUSED,FOCUSABLE IA2_STATE_EDITABLE,IA2_STATE_INVALID_ENTRY,IA2_STATE_REQUIRED,IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE
+EVENT_SYSTEM_ALERT on role=ROLE_SYSTEM_ALERT name="Please enter pet name"
diff --git a/content/test/data/forms/form_controls_browsertest_meter.png b/content/test/data/forms/form_controls_browsertest_meter.png
index 306c107..4eba96d 100644
--- a/content/test/data/forms/form_controls_browsertest_meter.png
+++ b/content/test/data/forms/form_controls_browsertest_meter.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_meter_android.png b/content/test/data/forms/form_controls_browsertest_meter_android.png
index 139e608..fa1aed8 100644
--- a/content/test/data/forms/form_controls_browsertest_meter_android.png
+++ b/content/test/data/forms/form_controls_browsertest_meter_android.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_meter_android_T.png b/content/test/data/forms/form_controls_browsertest_meter_android_T.png
index 20fb259..3d2c888d 100644
--- a/content/test/data/forms/form_controls_browsertest_meter_android_T.png
+++ b/content/test/data/forms/form_controls_browsertest_meter_android_T.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_meter_fuchsia.png b/content/test/data/forms/form_controls_browsertest_meter_fuchsia.png
index 1b00a8b..29a98995 100644
--- a/content/test/data/forms/form_controls_browsertest_meter_fuchsia.png
+++ b/content/test/data/forms/form_controls_browsertest_meter_fuchsia.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_meter_linux.png b/content/test/data/forms/form_controls_browsertest_meter_linux.png
index e141496..f37b800 100644
--- a/content/test/data/forms/form_controls_browsertest_meter_linux.png
+++ b/content/test/data/forms/form_controls_browsertest_meter_linux.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_meter_mac.png b/content/test/data/forms/form_controls_browsertest_meter_mac.png
index a1c0045..22ae801 100644
--- a/content/test/data/forms/form_controls_browsertest_meter_mac.png
+++ b/content/test/data/forms/form_controls_browsertest_meter_mac.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_meter_win.png b/content/test/data/forms/form_controls_browsertest_meter_win.png
index 73b8845..8921663 100644
--- a/content/test/data/forms/form_controls_browsertest_meter_win.png
+++ b/content/test/data/forms/form_controls_browsertest_meter_win.png
Binary files differ
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index ec72451..bde4934 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -209,6 +209,7 @@
 
 # Flaky failure on Mac Intel ASAN - DevTools Serialization Crash
 crbug.com/338574390 [ mac intel asan ] ContextLost_WebGLContextRestoredInHiddenTab [ Failure ]
+crbug.com/338574390 [ mac intel asan ] ContextLost_WebGPUBlockedAfterJSNavigation [ Failure ]
 
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index bcae75c9..28a7da4 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -129,7 +129,7 @@
 crbug.com/1470336 [ android-oreo android-nexus-5x ] WebCodecs_EncodingRateControl_vp8_prefer-hardware_constant_3000000 [ Failure ]
 crbug.com/1474916 [ android-nexus-5x ] WebCodecs_EncodeColorSpace_avc1.42001E_prefer-hardware [ Failure ]
 
-crbug.com/361615553 [ chromeos chromeos-board-jacuzzi ] WebCodecs_* [ Failure ]
+crbug.com/1486660 [ chromeos chromeos-board-jacuzzi ] WebCodecs_EncodeColorSpace_avc1.42001E_prefer-hardware [ Failure ]
 
 crbug.com/1466102 [ nvidia-0x2184 nvidia_lt_31.0.15.4601 win ] WebCodecs_FrameSizeChange_hvc1.1.6.L123.00_* [ Failure ]
 crbug.com/1517101 [ win amd-0x7340 ] WebCodecs_FrameSizeChange_hvc1.1.6.L123.00_* [ Failure ]
diff --git a/device/fido/enclave/types.cc b/device/fido/enclave/types.cc
index 37e9c1e..4039df9ec 100644
--- a/device/fido/enclave/types.cc
+++ b/device/fido/enclave/types.cc
@@ -7,18 +7,25 @@
 #include "components/sync/protocol/webauthn_credential_specifics.pb.h"
 
 namespace device::enclave {
+
 EnclaveIdentity::EnclaveIdentity() = default;
-EnclaveIdentity::~EnclaveIdentity() = default;
 EnclaveIdentity::EnclaveIdentity(const EnclaveIdentity&) = default;
+EnclaveIdentity::EnclaveIdentity(EnclaveIdentity&&) = default;
+EnclaveIdentity& EnclaveIdentity::operator=(const EnclaveIdentity&) = default;
+EnclaveIdentity& EnclaveIdentity::operator=(EnclaveIdentity&&) = default;
+EnclaveIdentity::~EnclaveIdentity() = default;
+
 ClientSignature::ClientSignature() = default;
 ClientSignature::~ClientSignature() = default;
 ClientSignature::ClientSignature(const ClientSignature&) = default;
 ClientSignature::ClientSignature(ClientSignature&&) = default;
+
 ClaimedPIN::ClaimedPIN(std::vector<uint8_t> in_pin_claim,
                        std::vector<uint8_t> in_wrapped_pin)
     : pin_claim(std::move(in_pin_claim)),
       wrapped_pin(std::move(in_wrapped_pin)) {}
 ClaimedPIN::~ClaimedPIN() = default;
+
 CredentialRequest::CredentialRequest() = default;
 CredentialRequest::~CredentialRequest() = default;
 CredentialRequest::CredentialRequest(CredentialRequest&&) = default;
diff --git a/device/fido/enclave/types.h b/device/fido/enclave/types.h
index c4fa5724..00cd7c3 100644
--- a/device/fido/enclave/types.h
+++ b/device/fido/enclave/types.h
@@ -27,8 +27,11 @@
 // connect to an enclave.
 struct COMPONENT_EXPORT(DEVICE_FIDO) EnclaveIdentity {
   EnclaveIdentity();
-  ~EnclaveIdentity();
   EnclaveIdentity(const EnclaveIdentity&);
+  EnclaveIdentity(EnclaveIdentity&&);
+  EnclaveIdentity& operator=(const EnclaveIdentity&);
+  EnclaveIdentity& operator=(EnclaveIdentity&&);
+  ~EnclaveIdentity();
 
   GURL url;
   std::array<uint8_t, kP256X962Length> public_key;
diff --git a/docs/security/faq.md b/docs/security/faq.md
index 0583d47..e122c012 100644
--- a/docs/security/faq.md
+++ b/docs/security/faq.md
@@ -1004,6 +1004,21 @@
 they’re screen sharing. We don’t do this on all platforms because we consider
 such risks greater on some than on others.
 
+
+<a name="TOC-On-some-websites-I-can-use-a-passkey-without-passing-a-lock-screen-or-biometric-challenge-is-this-a-security-bug"></a>
+### On some websites, I can use passkeys without passing a lock screen or biometric challenge. Is this a security bug?
+
+Probably not. When a website requests a passkeys signature, it can choose
+whether the authenticator should perform user verification (e.g. with a local
+user lock screen challenge). Unless the website sets user verification parameter
+in the request to 'required', the passkey authenticator can choose to skip the
+lock screen challenge. Authenticators commonly skip an optional challenge if
+biometrics are unavailable (e.g. on a laptop with a closed lid).
+
+If you can demonstrate bypassing the user verification challenge where the
+request user verification parameter is set to 'required', please
+[report it](https://issues.chromium.org/issues/new?noWizard=true&component=1363614&template=1922342).
+
 ## Other
 
 <a name="TOC-What-is-the-security-story-for-Service-Workers-"></a>
diff --git a/docs/testing/run_web_platform_tests.md b/docs/testing/run_web_platform_tests.md
index 3e2617f0..e4c9f53 100644
--- a/docs/testing/run_web_platform_tests.md
+++ b/docs/testing/run_web_platform_tests.md
@@ -108,6 +108,11 @@
 
 ## Running Web Platform Tests with Chrome Android
 
+*** note
+Android support is currently experimental and not on CQ/CI yet.
+See https://crbug.com/40279492 for updates.
+***
+
 See [here](./run_web_platform_tests_with_chrome_android.md) for Android specific instructions.
 
 ## Running Web Platform Tests with WebView
@@ -125,11 +130,25 @@
 
 [these debugging tips]: /docs/linux/debugging.md
 
+## FAQ
+
+* Do headless shell and Chrome support MojoJS bindings?
+    * Yes.
+      `run_wpt_tests.py` enables the `MojoJS` and `MojoJSTest` features and
+      serves `//out/<target>/gen/` as `/gen/` in wptserve.
+      However, in the public WPT suite, testdriver.js APIs backed by standard
+      WebDriver endpoints should be preferred over polyfills backed by MojoJS,
+      which are Chromium-specific.
+      See https://github.com/web-platform-tests/rfcs/issues/172 for additional
+      discussion.
+
 ## Known Issues
 
-Please [file bugs and feature requests](https://crbug.com/new) against
-[`Blink>Infra` with the `wptrunner`
-label](https://bugs.chromium.org/p/chromium/issues/list?q=component%3ABlink%3EInfra%20label%3Awptrunner&can=2).
+The [`wptrunner-migration`
+hostlist](https://issues.chromium.org/hotlists/6224346) tracks test results
+where headless shell and content shell differ.
+For runner bugs and feature requests, please file [an issue against
+`Blink>Infra`](https://issues.chromium.org/issues/new?component=1456928&template=1923166).
 
 [protocol mode]: /content/web_test/browser/test_info_extractor.h
 [debug renderer]: /third_party/blink/tools/debug_renderer
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index bcdc3956..0e4b66b 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -217,94 +217,119 @@
   public_deps = [ "//content/public/browser" ]
 }
 
-# TODO(https://crbug.com/356905053): Gradually include more of these sources
-# in the desktop-android build.
-if (enable_extensions) {
-  repack("shell_and_test_pak") {
-    testonly = true
+repack("shell_and_test_pak") {
+  testonly = true
 
-    sources = [
+  sources = [
+    "$root_gen_dir/content/content_resources.pak",
+    "$root_gen_dir/content/shell/shell_resources.pak",
+    "$root_gen_dir/extensions/extensions_browser_resources_100_percent.pak",
+    "$root_gen_dir/extensions/extensions_renderer_resources.pak",
+    "$root_gen_dir/extensions/extensions_resources.pak",
+    "$root_gen_dir/extensions/strings/extensions_strings_en-US.pak",
+    "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
+    "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
+    "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_100_percent.pak",
+    "$root_gen_dir/third_party/blink/public/strings/blink_strings_en-US.pak",
+    "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
+    "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
+    "$root_gen_dir/ui/strings/ax_strings_en-US.pak",
+    "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
+  ]
+
+  output = "$root_out_dir/extensions_shell_and_test.pak"
+
+  deps = [
+    ":extensions_resources",
+    "//content:content_resources",
+    "//content/shell:resources",
+    "//extensions/strings",
+    "//mojo/public/js:resources",
+    "//third_party/blink/public:resources",
+    "//third_party/blink/public:scaled_resources_100_percent",
+    "//third_party/blink/public/strings",
+    "//ui/resources",
+    "//ui/strings",
+  ]
+
+  # The following resources are not used by desktop-android build.
+  if (enable_extensions) {
+    sources += [
       "$root_gen_dir/content/browser/devtools/devtools_resources.pak",
-      "$root_gen_dir/content/content_resources.pak",
-      "$root_gen_dir/content/shell/shell_resources.pak",
       "$root_gen_dir/device/bluetooth/strings/bluetooth_strings_en-US.pak",
-      "$root_gen_dir/extensions/extensions_browser_resources_100_percent.pak",
-      "$root_gen_dir/extensions/extensions_renderer_resources.pak",
-      "$root_gen_dir/extensions/extensions_resources.pak",
       "$root_gen_dir/extensions/shell/app_shell_resources.pak",
-      "$root_gen_dir/extensions/strings/extensions_strings_en-US.pak",
-      "$root_gen_dir/mojo/public/js/mojo_bindings_resources.pak",
-      "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak",
-      "$root_gen_dir/third_party/blink/public/resources/blink_scaled_resources_100_percent.pak",
-      "$root_gen_dir/third_party/blink/public/strings/blink_strings_en-US.pak",
-      "$root_gen_dir/ui/resources/ui_resources_100_percent.pak",
-      "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak",
-      "$root_gen_dir/ui/strings/ax_strings_en-US.pak",
-      "$root_gen_dir/ui/strings/ui_strings_en-US.pak",
     ]
 
-    output = "$root_out_dir/extensions_shell_and_test.pak"
-
-    deps = [
-      ":extensions_resources",
-      "//content:content_resources",
+    deps += [
       "//content/browser/devtools:devtools_resources",
-      "//content/shell:resources",
       "//device/bluetooth/strings",
       "//extensions/shell:resources",
-      "//extensions/strings",
-      "//mojo/public/js:resources",
-      "//third_party/blink/public:resources",
-      "//third_party/blink/public:scaled_resources_100_percent",
-      "//third_party/blink/public/strings",
-      "//ui/resources",
-      "//ui/strings",
+    ]
+  }
+}
+
+test("extensions_unittests") {
+  use_xvfb = use_xvfb_in_this_config
+
+  sources = [
+    "test/extensions_unittests_main.cc",
+    "test/flakiness_test_util_test.cc",
+    "test/logging_timer_unittest.cc",
+  ]
+
+  data = [
+    "test/data/",
+    "//chrome/test/data/extensions/",
+    "$root_out_dir/content_shell.pak",
+    "$root_out_dir/extensions_shell_and_test.pak",
+  ]
+
+  deps = [
+    ":shell_and_test_pak",
+    ":test_support",
+    "//base/test:test_support",
+    "//build:android_buildflags",
+    "//content/public/common",
+    "//content/test:test_support",
+    "//extensions/common",
+    "//extensions/common:unit_tests",
+    "//services/data_decoder/public/cpp:test_support",
+    "//services/service_manager/public/cpp/test:test_support",
+    "//ui/gl:test_support",
+  ]
+
+  data_deps = [
+    "//testing/buildbot/filters:extensions_unittests_filters",
+    "//third_party/angle:includes",
+  ]
+
+  if (is_chromeos) {
+    deps += [
+      "//chrome/browser/chromeos/extensions/telemetry/api:extensions_unittests",
     ]
   }
 
-  test("extensions_unittests") {
-    use_xvfb = use_xvfb_in_this_config
-
-    sources = [
-      "test/extensions_unittests_main.cc",
-      "test/flakiness_test_util_test.cc",
-      "test/logging_timer_unittest.cc",
+  if (is_android) {
+    deps += [
+      "//gin:v8_snapshot_assets",
+      "//services/data_decoder/public/cpp/android:safe_json_java",
+      "//ui/android:ui_full_java",
     ]
+  }
 
-    data = [
-      "test/data/",
-      "//chrome/test/data/extensions/",
-      "$root_out_dir/content_shell.pak",
-      "$root_out_dir/extensions_shell_and_test.pak",
-    ]
-
-    deps = [
-      ":extensions_resources",
-      ":shell_and_test_pak",
-      ":test_support",
-      "//base/test:test_support",
-      "//content/public/common",
-      "//content/test:test_support",
+  # TODO(https://crbug.com/356905053): Gradually include more of these sources
+  # in the desktop-android build.
+  if (enable_extensions) {
+    deps += [
       "//extensions/browser:unit_tests",
-      "//extensions/common",
-      "//extensions/common:unit_tests",
       "//extensions/renderer:unit_tests",
       "//extensions/shell:unit_tests",
-      "//services/data_decoder/public/cpp:test_support",
-      "//services/service_manager/public/cpp/test:test_support",
-      "//ui/gl:test_support",
     ]
-
-    data_deps = [
-      "//testing/buildbot/filters:extensions_unittests_filters",
-      "//third_party/angle:includes",
-    ]
-
-    if (is_chromeos) {
-      deps += [ "//chrome/browser/chromeos/extensions/telemetry/api:extensions_unittests" ]
-    }
   }
+}
 
+if (enable_extensions) {
+  # Browser tests are not currently supported in desktop-android builds.
   test("extensions_browsertests") {
     use_xvfb = use_xvfb_in_this_config
 
diff --git a/extensions/browser/app_window/app_window_geometry_cache.cc b/extensions/browser/app_window/app_window_geometry_cache.cc
index 4c120655..7002275 100644
--- a/extensions/browser/app_window/app_window_geometry_cache.cc
+++ b/extensions/browser/app_window/app_window_geometry_cache.cc
@@ -121,7 +121,7 @@
       value.Set("screen_bounds_y", screen_bounds.y());
       value.Set("screen_bounds_w", screen_bounds.width());
       value.Set("screen_bounds_h", screen_bounds.height());
-      value.Set("state", data_it->second.window_state);
+      value.Set("state", static_cast<int>(data_it->second.window_state));
       value.Set("ts", base::TimeToValue(data_it->second.last_change));
       dict.Set(data_it->first, std::move(value));
 
diff --git a/extensions/browser/app_window/app_window_geometry_cache_unittest.cc b/extensions/browser/app_window/app_window_geometry_cache_unittest.cc
index a0a28ec..27d2501 100644
--- a/extensions/browser/app_window/app_window_geometry_cache_unittest.cc
+++ b/extensions/browser/app_window/app_window_geometry_cache_unittest.cc
@@ -92,7 +92,7 @@
   value.Set("screen_bounds_y", screen_bounds.y());
   value.Set("screen_bounds_w", screen_bounds.width());
   value.Set("screen_bounds_h", screen_bounds.height());
-  value.Set("state", state);
+  value.Set("state", static_cast<int>(state));
   dict.Set(window_id, std::move(value));
   extension_prefs_->SetGeometryCache(extension_id, std::move(dict));
   LoadExtension(extension_id);
@@ -285,7 +285,8 @@
             dict->FindIntByDottedPath(window_id + ".screen_bounds_w"));
   ASSERT_EQ(screen_bounds.height(),
             dict->FindIntByDottedPath(window_id + ".screen_bounds_h"));
-  ASSERT_EQ(state, dict->FindIntByDottedPath(window_id + ".state"));
+  ASSERT_EQ(static_cast<int>(state),
+            dict->FindIntByDottedPath(window_id + ".state"));
 
   // reload extension
   LoadExtension(extension_id);
diff --git a/extensions/browser/core_browser_context_keyed_service_factories.cc b/extensions/browser/core_browser_context_keyed_service_factories.cc
index 03c7ecf..e5d0e2e50 100644
--- a/extensions/browser/core_browser_context_keyed_service_factories.cc
+++ b/extensions/browser/core_browser_context_keyed_service_factories.cc
@@ -11,6 +11,7 @@
 #include "extensions/browser/extension_prefs_helper_factory.h"
 #include "extensions/browser/extension_protocols.h"
 #include "extensions/browser/image_loader_factory.h"
+#include "extensions/browser/permissions_manager.h"
 #include "extensions/browser/process_manager_factory.h"
 #include "extensions/browser/renderer_startup_helper.h"
 #include "extensions/browser/service_worker/service_worker_keepalive.h"
@@ -49,6 +50,7 @@
 #if BUILDFLAG(ENABLE_GUEST_VIEW)
   MimeHandlerStreamManager::EnsureFactoryBuilt();
 #endif
+  PermissionsManager::GetFactory();
   ProcessManagerFactory::GetInstance();
   RendererStartupHelperFactory::GetInstance();
   ServiceWorkerKeepalive::EnsureShutdownNotifierFactoryBuilt();
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index 8486697..22285b61 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -1551,17 +1551,6 @@
 void WebViewGuest::EnterFullscreenModeForTab(
     content::RenderFrameHost* requesting_frame,
     const blink::mojom::FullscreenOptions& options) {
-  // Ask the embedder for permission.
-  base::Value::Dict request_info;
-  const GURL& origin =
-      requesting_frame->GetLastCommittedURL().DeprecatedGetOriginAsURL();
-  request_info.Set(webview::kOrigin, origin.spec());
-  web_view_permission_helper_->RequestPermission(
-      WEB_VIEW_PERMISSION_TYPE_FULLSCREEN, std::move(request_info),
-      base::BindOnce(&WebViewGuest::OnFullscreenPermissionDecided,
-                     weak_ptr_factory_.GetWeakPtr()),
-      false /* allowed_by_default */);
-
   // TODO(lazyboy): Right now the guest immediately goes fullscreen within its
   // bounds. If the embedder denies the permission then we will see a flicker.
   // Once we have the ability to "cancel" a renderer/ fullscreen request:
@@ -1569,6 +1558,12 @@
   // Calling SetFullscreenState(true) once the embedder allowed the request.
   // Otherwise we would cancel renderer/ fullscreen if the embedder denied.
   SetFullscreenState(true);
+
+  // Ask the embedder for permission.
+  web_view_permission_helper_->RequestFullscreenPermission(
+      requesting_frame->GetLastCommittedOrigin(),
+      base::BindOnce(&WebViewGuest::OnFullscreenPermissionDecided,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WebViewGuest::ExitFullscreenModeForTab(WebContents* web_contents) {
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
index cb97bad..0f69ef3 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
@@ -308,6 +308,13 @@
       url, allowed_by_default, std::move(callback));
 }
 
+void WebViewPermissionHelper::RequestFullscreenPermission(
+    const url::Origin& requesting_origin,
+    PermissionResponseCallback callback) {
+  web_view_permission_helper_delegate_->RequestFullscreenPermission(
+      requesting_origin, std::move(callback));
+}
+
 int WebViewPermissionHelper::RequestPermission(
     WebViewPermissionType permission_type,
     base::Value::Dict request_info,
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.h b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
index 67f730c..8a6029aa 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
@@ -18,6 +18,10 @@
 #include "ppapi/buildflags/buildflags.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 
+namespace url {
+class Origin;
+}  // namespace url
+
 namespace extensions {
 
 class WebViewGuest;
@@ -102,6 +106,9 @@
                                    bool allowed_by_default,
                                    base::OnceCallback<void(bool)> callback);
 
+  void RequestFullscreenPermission(const url::Origin& requesting_origin,
+                                   PermissionResponseCallback callback);
+
   enum PermissionResponseAction { DENY, ALLOW, DEFAULT };
 
   enum SetPermissionResult {
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
index 6c4d810e..1ee14bb 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
@@ -17,6 +17,10 @@
 class WebContents;
 }  // namespace content
 
+namespace url {
+class Origin;
+}  // namespace url
+
 class GURL;
 
 namespace extensions {
@@ -68,6 +72,10 @@
       bool allowed_by_default,
       base::OnceCallback<void(bool)> callback) {}
 
+  virtual void RequestFullscreenPermission(
+      const url::Origin& requesting_origin,
+      WebViewPermissionHelper::PermissionResponseCallback callback) {}
+
   // Called when file system access is requested by the guest content using the
   // asynchronous HTML5 file system API. The request is plumbed through the
   // <webview> permission request API. The request will be:
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index bdc67e77..00092ad 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -605,124 +605,125 @@
   ]
 }
 
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "api/commands/commands_manifest_unittest.cc",
+    "api/declarative/declarative_manifest_unittest.cc",
+    "api/declarative_net_request/dnr_manifest_unittest.cc",
+    "api/messaging/messaging_endpoint_unittest.cc",
+    "api/printer_provider/usb_printer_manifest_unittest.cc",
+    "api/scripts_internal/script_serialization_unittest.cc",
+    "api/sockets/sockets_manifest_permission_unittest.cc",
+    "command_unittest.cc",
+    "component_extension_url_pattern_unittest.cc",
+    "crash_keys_unittest.cc",
+    "csp_validator_unittest.cc",
+    "error_utils_unittest.cc",
+    "event_filter_unittest.cc",
+    "extension_builder_unittest.cc",
+    "extension_l10n_util_unittest.cc",
+    "extension_resource_path_normalizer_unittest.cc",
+    "extension_resource_unittest.cc",
+    "extension_set_unittest.cc",
+    "extension_unittest.cc",
+    "extension_urls_unittest.cc",
+    "feature_switch_unittest.cc",
+    "features/complex_feature_unittest.cc",
+    "features/feature_provider_unittest.cc",
+    "features/simple_feature_unittest.cc",
+    "file_util_unittest.cc",
+    "hashed_extension_id_unittest.cc",
+    "icons/extension_icon_set_unittest.cc",
+    "image_util_unittest.cc",
+    "manifest_handler_perf_test.cc",
+    "manifest_handler_unittest.cc",
+    "manifest_handlers/content_capabilities_manifest_unittest.cc",
+    "manifest_handlers/csp_info_unittest.cc",
+    "manifest_handlers/default_locale_manifest_unittest.cc",
+    "manifest_handlers/extension_action_handler_unittest.cc",
+    "manifest_handlers/extension_action_page_action_unittest.cc",
+    "manifest_handlers/extension_manifests_icon_variants_unittest.cc",
+    "manifest_handlers/externally_connectable_unittest.cc",
+    "manifest_handlers/homepage_url_unittest.cc",
+    "manifest_handlers/icons_handler_unittest.cc",
+    "manifest_handlers/incognito_manifest_unittest.cc",
+    "manifest_handlers/kiosk_mode_info_unittest.cc",
+    "manifest_handlers/manifest_url_about_unittest.cc",
+    "manifest_handlers/manifest_v3_permissions_unittest.cc",
+    "manifest_handlers/mime_types_handler_unittest.cc",
+    "manifest_handlers/oauth2_manifest_unittest.cc",
+    "manifest_handlers/replacement_apps_unittest.cc",
+    "manifest_handlers/requirements_unittest.cc",
+    "manifest_handlers/shared_module_manifest_unittest.cc",
+    "manifest_handlers/trial_tokens_unittest.cc",
+    "manifest_handlers/update_url_unittest.cc",
+    "manifest_unittest.cc",
+    "message_bundle_unittest.cc",
+    "mojom/message_port_mojom_traits_unittest.cc",
+    "mojom/permission_set_mojom_traits_unittest.cc",
+    "mojom/url_pattern_set_mojom_traits_unittest.cc",
+    "permissions/api_permission_set_unittest.cc",
+    "permissions/api_permission_unittest.cc",
+    "permissions/base_set_operators_unittest.cc",
+    "permissions/manifest_permission_set_unittest.cc",
+    "permissions/socket_permission_unittest.cc",
+    "permissions/usb_device_permission_unittest.cc",
+    "stack_frame_unittest.cc",
+    "url_pattern_set_unittest.cc",
+    "url_pattern_unittest.cc",
+    "user_script_unittest.cc",
+    "utils/extension_types_utils_unittest.cc",
+    "value_counter_unittest.cc",
+  ]
+
+  deps = [
+    ":common",
+    ":test_support",
+    "//base",
+    "//base:i18n",
+    "//base/test:test_support",
+    "//build:android_buildflags",
+    "//components/crx_file",
+    "//components/version_info:version_info",
+    "//content/test:test_support",
+    "//extensions:extensions_resources",
+    "//extensions/common:mojom",
+    "//extensions/common/api",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//tools/json_schema_compiler:generated_api_util",
+
+    # TODO(brettw) these tests should not be including headers from browser.
+    "//extensions:test_support",
+    "//extensions/browser",
+    "//extensions/strings",
+    "//ipc",
+    "//services/device/public/cpp:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/zlib/google:compression_utils",
+    "//ui/base",
+    "//ui/gfx",
+    "//url",
+  ]
+
+  if (is_chromeos) {
+    sources += [ "manifest_handlers/file_handler_manifest_unittest.cc" ]
+  }
+
+  if (is_chromeos_ash) {
+    sources += [ "manifest_handlers/action_handlers_handler_unittest.cc" ]
+  }
+
+  data = [
+    "//extensions/common/mojom/api_permission_id.mojom",
+    "//tools/metrics/histograms/metadata/extensions/enums.xml",
+  ]
+}
+
 # TODO(https://crbug.com/356905053): Gradually include more of these sources
 # in the desktop-android build.
 if (enable_extensions) {
-  source_set("unit_tests") {
-    testonly = true
-    sources = [
-      "api/commands/commands_manifest_unittest.cc",
-      "api/declarative/declarative_manifest_unittest.cc",
-      "api/declarative_net_request/dnr_manifest_unittest.cc",
-      "api/messaging/messaging_endpoint_unittest.cc",
-      "api/printer_provider/usb_printer_manifest_unittest.cc",
-      "api/scripts_internal/script_serialization_unittest.cc",
-      "api/sockets/sockets_manifest_permission_unittest.cc",
-      "command_unittest.cc",
-      "component_extension_url_pattern_unittest.cc",
-      "crash_keys_unittest.cc",
-      "csp_validator_unittest.cc",
-      "error_utils_unittest.cc",
-      "event_filter_unittest.cc",
-      "extension_builder_unittest.cc",
-      "extension_l10n_util_unittest.cc",
-      "extension_resource_path_normalizer_unittest.cc",
-      "extension_resource_unittest.cc",
-      "extension_set_unittest.cc",
-      "extension_unittest.cc",
-      "extension_urls_unittest.cc",
-      "feature_switch_unittest.cc",
-      "features/complex_feature_unittest.cc",
-      "features/feature_provider_unittest.cc",
-      "features/simple_feature_unittest.cc",
-      "file_util_unittest.cc",
-      "hashed_extension_id_unittest.cc",
-      "icons/extension_icon_set_unittest.cc",
-      "image_util_unittest.cc",
-      "manifest_handler_perf_test.cc",
-      "manifest_handler_unittest.cc",
-      "manifest_handlers/content_capabilities_manifest_unittest.cc",
-      "manifest_handlers/csp_info_unittest.cc",
-      "manifest_handlers/default_locale_manifest_unittest.cc",
-      "manifest_handlers/extension_action_handler_unittest.cc",
-      "manifest_handlers/extension_action_page_action_unittest.cc",
-      "manifest_handlers/extension_manifests_icon_variants_unittest.cc",
-      "manifest_handlers/externally_connectable_unittest.cc",
-      "manifest_handlers/homepage_url_unittest.cc",
-      "manifest_handlers/icons_handler_unittest.cc",
-      "manifest_handlers/incognito_manifest_unittest.cc",
-      "manifest_handlers/kiosk_mode_info_unittest.cc",
-      "manifest_handlers/manifest_url_about_unittest.cc",
-      "manifest_handlers/manifest_v3_permissions_unittest.cc",
-      "manifest_handlers/mime_types_handler_unittest.cc",
-      "manifest_handlers/oauth2_manifest_unittest.cc",
-      "manifest_handlers/replacement_apps_unittest.cc",
-      "manifest_handlers/requirements_unittest.cc",
-      "manifest_handlers/shared_module_manifest_unittest.cc",
-      "manifest_handlers/trial_tokens_unittest.cc",
-      "manifest_handlers/update_url_unittest.cc",
-      "manifest_unittest.cc",
-      "message_bundle_unittest.cc",
-      "mojom/message_port_mojom_traits_unittest.cc",
-      "mojom/permission_set_mojom_traits_unittest.cc",
-      "mojom/url_pattern_set_mojom_traits_unittest.cc",
-      "permissions/api_permission_set_unittest.cc",
-      "permissions/api_permission_unittest.cc",
-      "permissions/base_set_operators_unittest.cc",
-      "permissions/manifest_permission_set_unittest.cc",
-      "permissions/socket_permission_unittest.cc",
-      "permissions/usb_device_permission_unittest.cc",
-      "stack_frame_unittest.cc",
-      "url_pattern_set_unittest.cc",
-      "url_pattern_unittest.cc",
-      "user_script_unittest.cc",
-      "utils/extension_types_utils_unittest.cc",
-      "value_counter_unittest.cc",
-    ]
-
-    deps = [
-      ":common",
-      ":test_support",
-      "//base",
-      "//base:i18n",
-      "//base/test:test_support",
-      "//components/crx_file",
-      "//components/version_info:version_info",
-      "//content/test:test_support",
-      "//extensions:extensions_resources",
-      "//extensions/common:mojom",
-      "//extensions/common/api",
-      "//mojo/public/cpp/test_support:test_utils",
-      "//tools/json_schema_compiler:generated_api_util",
-
-      # TODO(brettw) these tests should not be including headers from browser.
-      "//extensions:test_support",
-      "//extensions/browser",
-      "//extensions/strings",
-      "//ipc",
-      "//services/device/public/cpp:test_support",
-      "//testing/gmock",
-      "//testing/gtest",
-      "//third_party/zlib/google:compression_utils",
-      "//ui/base",
-      "//ui/gfx",
-      "//url",
-    ]
-
-    if (is_chromeos) {
-      sources += [ "manifest_handlers/file_handler_manifest_unittest.cc" ]
-    }
-
-    if (is_chromeos_ash) {
-      sources += [ "manifest_handlers/action_handlers_handler_unittest.cc" ]
-    }
-
-    data = [
-      "//extensions/common/mojom/api_permission_id.mojom",
-      "//tools/metrics/histograms/metadata/extensions/enums.xml",
-    ]
-  }
-
   fuzzer_test("extension_fuzzer") {
     sources = [ "extension_fuzzer.cc" ]
     deps = [
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 399334b..6e111dd 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -383,7 +383,7 @@
       "chrome-untrusted://boca-app/*",
       "chrome-untrusted://compose/*",
       "chrome-untrusted://help-app/*",
-      "chrome-untrusted://lens/*",
+      "chrome-untrusted://lens-overlay/*",
       "chrome-untrusted://media-app/*",
       "chrome-untrusted://mako/*",
       "chrome-untrusted://projector/*",
diff --git a/extensions/common/command_unittest.cc b/extensions/common/command_unittest.cc
index f7c1033..c8f9d81d 100644
--- a/extensions/common/command_unittest.cc
+++ b/extensions/common/command_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "build/android_buildflags.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -206,7 +207,15 @@
     CheckParse(kTests[i], i, false, all_platforms);
 }
 
-TEST(CommandTest, ExtensionCommandParsingFallback) {
+// TODO(https://crbug.com/356905053): Add/adjust command key support on
+// desktop-android platform.
+#if BUILDFLAG(IS_DESKTOP_ANDROID)
+#define MAYBE_ExtensionCommandParsingFallback \
+  DISABLED_ExtensionCommandParsingFallback
+#else
+#define MAYBE_ExtensionCommandParsingFallback ExtensionCommandParsingFallback
+#endif
+TEST(CommandTest, MAYBE_ExtensionCommandParsingFallback) {
   std::string description = "desc";
   std::string command_name = "foo";
 
diff --git a/extensions/common/features/feature_provider_unittest.cc b/extensions/common/features/feature_provider_unittest.cc
index 57041f4..0e36893 100644
--- a/extensions/common/features/feature_provider_unittest.cc
+++ b/extensions/common/features/feature_provider_unittest.cc
@@ -12,6 +12,7 @@
 
 #include "base/ranges/algorithm.h"
 #include "base/test/bind.h"
+#include "build/android_buildflags.h"
 #include "build/build_config.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/extensions_client.h"
@@ -129,7 +130,8 @@
   // NOT_FOUND_IN_ALLOWLIST.
   // TODO(crbug.com/40198321): Port //device/bluetooth to Fuchsia to
   // enable bluetooth extensions.
-#if !BUILDFLAG(IS_FUCHSIA)
+  // bluetoothPrivate is unsupported in desktop-android build.
+#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_DESKTOP_ANDROID)
   feature = provider->GetFeature("bluetoothPrivate");
   ASSERT_TRUE(feature);
   EXPECT_EQ(Feature::NOT_FOUND_IN_ALLOWLIST,
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index d8cbace..a3ced10 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -985,6 +985,17 @@
 
     RendererExtensionRegistry* extension_registry =
         RendererExtensionRegistry::Get();
+
+    // The order of setting the token before inserting the extension is
+    // intentional so that DidInitializeServiceWorkerContextOnWorkerThread()
+    // will pause the worker evaluation
+    // (WillEvaluateServiceWorkerOnWorkerThread()) from running before we have
+    // these two necessary pieces of information for setting up the extension
+    // bindings.
+    if (worker_activation_token.has_value()) {
+      extension_registry->SetWorkerActivationToken(
+          extension, std::move(*worker_activation_token));
+    }
     // TODO(jlulejian): This is deliberately a CHECK because other parts of the
     // renderer assume that an extension has been loaded (e.g. specifically
     // worker script evaluation process). If this fails, other CHECK()s and
@@ -993,11 +1004,6 @@
 
     unloaded_extensions_.erase(extension->id());
 
-    if (worker_activation_token.has_value()) {
-      extension_registry->SetWorkerActivationToken(
-          extension, std::move(*worker_activation_token));
-    }
-
     ExtensionsRendererClient::Get()->OnExtensionLoaded(*extension);
 
     // Resume service worker if it is suspended.
diff --git a/extensions/renderer/renderer_extension_registry.cc b/extensions/renderer/renderer_extension_registry.cc
index 82c96eba..18826c5a 100644
--- a/extensions/renderer/renderer_extension_registry.cc
+++ b/extensions/renderer/renderer_extension_registry.cc
@@ -50,6 +50,9 @@
 bool RendererExtensionRegistry::Insert(
     const scoped_refptr<const Extension>& extension) {
   DCHECK(content::RenderThread::Get());
+  // TODO(crbug.com/357889496): This should check to confirm that the activation
+  // token is set for service worker based extensions (except for incognito
+  // spanning mode), and is not set for non service worker based extensions.
   base::AutoLock lock(lock_);
   return extensions_.Insert(extension);
 }
@@ -100,7 +103,6 @@
     const scoped_refptr<const Extension>& extension,
     base::UnguessableToken worker_activation_token) {
   DCHECK(content::RenderThread::Get());
-  DCHECK(Contains(extension->id()));
   DCHECK(BackgroundInfo::IsServiceWorkerBased(extension.get()));
 
   base::AutoLock lock(lock_);
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index ff7ee45..6ba3a2d 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -318,6 +318,7 @@
     "//apps",
     "//base",
     "//base/test:test_support",
+    "//build:android_buildflags",
     "//build:chromeos_buildflags",
     "//components/crx_file",
     "//components/feedback",
diff --git a/extensions/test/extensions_unittests_main.cc b/extensions/test/extensions_unittests_main.cc
index ce77637..2b7d855 100644
--- a/extensions/test/extensions_unittests_main.cc
+++ b/extensions/test/extensions_unittests_main.cc
@@ -9,6 +9,7 @@
 #include "base/path_service.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_io_thread.h"
+#include "build/android_buildflags.h"
 #include "build/buildflag.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "content/public/common/content_client.h"
@@ -83,6 +84,11 @@
 
   base::FilePath extensions_shell_and_test_pak_path;
   base::PathService::Get(base::DIR_ASSETS, &extensions_shell_and_test_pak_path);
+#if BUILDFLAG(IS_DESKTOP_ANDROID)
+  // On Android all pak files are inside the paks folder.
+  extensions_shell_and_test_pak_path =
+      extensions_shell_and_test_pak_path.Append(FILE_PATH_LITERAL("paks"));
+#endif
   ui::ResourceBundle::InitSharedInstanceWithPakPath(
       extensions_shell_and_test_pak_path.AppendASCII(
           "extensions_shell_and_test.pak"));
diff --git a/fuchsia_web/av_testing/.style.yapf b/fuchsia_web/av_testing/.style.yapf
new file mode 100644
index 0000000..557fa7b
--- /dev/null
+++ b/fuchsia_web/av_testing/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = pep8
diff --git a/fuchsia_web/av_testing/BUILD.gn b/fuchsia_web/av_testing/BUILD.gn
index 948e567..c53afa6 100644
--- a/fuchsia_web/av_testing/BUILD.gn
+++ b/fuchsia_web/av_testing/BUILD.gn
@@ -7,4 +7,9 @@
 python_library("av_sync_tests") {
   testonly = true
   pydeps_file = "av_sync_tests.pydeps"
+  data_deps = [
+    "//build/config/fuchsia:deployment_resources",
+    "//content/test:fuchsia_telemetry_test_data",
+    "//fuchsia_web/shell",
+  ]
 }
diff --git a/fuchsia_web/av_testing/av_sync_tests.py b/fuchsia_web/av_testing/av_sync_tests.py
index e394009..49452eb 100755
--- a/fuchsia_web/av_testing/av_sync_tests.py
+++ b/fuchsia_web/av_testing/av_sync_tests.py
@@ -2,15 +2,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.
-""" Execute Audio / Video performance tests against a smart display device. """
+""" Execute Audio / Video performance tests against a smart display device.
+    This script needs to be executed from the build output folder, e.g.
+    out/fuchsia/."""
 
+import os
+import subprocess
 import sys
+import time
 
+TEST_SCRIPTS_ROOT = os.path.join(os.path.dirname(__file__), '..', '..',
+                                 'build', 'fuchsia', 'test')
+sys.path.append(TEST_SCRIPTS_ROOT)
+
+from browser_runner import BrowserRunner
+from compatible_utils import running_unattended
+from run_webpage_test import WebpageTestRunner, capture_devtools_port
 
 # TODO(crbug.com/40935291): Implement the tests.
 def main() -> int:
-    print('Running Audio / Video performance tests.')
+    proc = subprocess.Popen([
+        os.path.join(TEST_SCRIPTS_ROOT,
+                     'run_test.py'), 'webpage', '--out-dir=.',
+        '--browser=web-engine-shell', '--device', '--logs-dir=/tmp'
+    ],
+                            env={
+                                **os.environ, 'CHROME_HEADLESS': '1'
+                            })
+    port = capture_devtools_port(proc, '/tmp')
+    print('DevTools is now running on ' + str(port))
+    if not running_unattended():
+        print('Sleep 60 seconds before shutting it down')
+        time.sleep(60)
+    proc.terminate()
+    proc.wait()
     return 0
 
+
 if __name__ == '__main__':
+    # TODO(crbug.com/40935291): Currently the machine is not running a fuchsia
+    # managed docker image, the FUCHSIA_NODENAME environment is not set.
+    if 'FUCHSIA_NODENAME' not in os.environ:
+        os.environ['FUCHSIA_NODENAME'] = 'fuchsia-ac67-8475-ee82'
     sys.exit(main())
diff --git a/fuchsia_web/av_testing/av_sync_tests.pydeps b/fuchsia_web/av_testing/av_sync_tests.pydeps
index eead68b..0354fe6 100644
--- a/fuchsia_web/av_testing/av_sync_tests.pydeps
+++ b/fuchsia_web/av_testing/av_sync_tests.pydeps
@@ -1,3 +1,9 @@
 # Generated by running:
 #   build/print_python_deps.py --root fuchsia_web/av_testing --output fuchsia_web/av_testing/av_sync_tests.pydeps fuchsia_web/av_testing/av_sync_tests.py
+../../build/fuchsia/test/browser_runner.py
+../../build/fuchsia/test/common.py
+../../build/fuchsia/test/compatible_utils.py
+../../build/fuchsia/test/ffx_integration.py
+../../build/fuchsia/test/run_webpage_test.py
+../../build/fuchsia/test/test_runner.py
 av_sync_tests.py
diff --git a/fuchsia_web/shell/BUILD.gn b/fuchsia_web/shell/BUILD.gn
index a0f2f1c0..a0cc192e3 100644
--- a/fuchsia_web/shell/BUILD.gn
+++ b/fuchsia_web/shell/BUILD.gn
@@ -18,6 +18,7 @@
     "//chrome/test:*",
     "//content/test:*",
     "//fuchsia_web:gn_all",
+    "//fuchsia_web/av_testing:*",
   ]
   deps = [ ":web_engine_shell" ]
 
diff --git a/google_apis/gaia/gaia_constants.cc b/google_apis/gaia/gaia_constants.cc
index 1dabd74..cf7698d 100644
--- a/google_apis/gaia/gaia_constants.cc
+++ b/google_apis/gaia/gaia_constants.cc
@@ -139,8 +139,8 @@
     "https://www.googleapis.com/auth/assistant-sdk-prototype";
 
 // OAuth2 scope for access to nearby devices (fast pair) APIs.
-const char kCloudPlatformProjectsOAuth2Scope[] =
-    "https://www.googleapis.com/auth/cloudplatformprojects";
+const char kNearbyDevicesOAuth2Scope[] =
+    "https://www.googleapis.com/auth/nearbydevices-pa";
 
 // OAuth2 scope for access to nearby sharing.
 const char kNearbyShareOAuth2Scope[] =
diff --git a/google_apis/gaia/gaia_constants.h b/google_apis/gaia/gaia_constants.h
index 199c0e9..8ac9346 100644
--- a/google_apis/gaia/gaia_constants.h
+++ b/google_apis/gaia/gaia_constants.h
@@ -63,7 +63,7 @@
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kDriveReadOnlyOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kAssistantOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS)
-extern const char kCloudPlatformProjectsOAuth2Scope[];
+extern const char kNearbyDevicesOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kNearbyShareOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kNearbyPresenceOAuth2Scope[];
 COMPONENT_EXPORT(GOOGLE_APIS) extern const char kGCMGroupServerOAuth2Scope[];
diff --git a/infra/config/generated/builder-owners/clank-engprod@google.com.txt b/infra/config/generated/builder-owners/clank-engprod@google.com.txt
index 033c955..d7a38c0cf 100644
--- a/infra/config/generated/builder-owners/clank-engprod@google.com.txt
+++ b/infra/config/generated/builder-owners/clank-engprod@google.com.txt
@@ -12,6 +12,7 @@
 ci/android-12-x64-rel
 ci/android-12l-landscape-x64-dbg-tests
 ci/android-12l-x64-dbg-tests
+ci/android-12l-x64-rel-cq
 ci/android-13-x64-rel
 ci/android-14-arm64-fyi-rel
 ci/android-14-arm64-rel
@@ -36,6 +37,7 @@
 ci/android-tablet-14-arm64-fyi-rel
 try/android-12-x64-rel
 try/android-12l-landscape-x64-dbg
+try/android-12l-x64-rel-cq
 try/android-13-x64-rel
 try/android-14-arm64-fyi-rel
 try/android-14-arm64-rel
diff --git "a/infra/config/generated/builders/ci/Linux Builder \050dbg\051/targets/chromium.linux.json" "b/infra/config/generated/builders/ci/Linux Builder \050dbg\051/targets/chromium.linux.json"
index 1adf256..34321f0 100644
--- "a/infra/config/generated/builders/ci/Linux Builder \050dbg\051/targets/chromium.linux.json"
+++ "b/infra/config/generated/builders/ci/Linux Builder \050dbg\051/targets/chromium.linux.json"
@@ -1261,7 +1261,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 15
         },
         "test": "blink_web_tests",
         "test_id_prefix": "ninja://:blink_web_tests/"
diff --git "a/infra/config/generated/builders/ci/Linux Tests \050dbg\051\0501\051/targets/chromium.linux.json" "b/infra/config/generated/builders/ci/Linux Tests \050dbg\051\0501\051/targets/chromium.linux.json"
index 5382d87..c66d9be 100644
--- "a/infra/config/generated/builders/ci/Linux Tests \050dbg\051\0501\051/targets/chromium.linux.json"
+++ "b/infra/config/generated/builders/ci/Linux Tests \050dbg\051\0501\051/targets/chromium.linux.json"
@@ -1256,7 +1256,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 15
         },
         "test": "blink_web_tests",
         "test_id_prefix": "ninja://:blink_web_tests/"
diff --git a/infra/config/generated/builders/ci/android-12l-x64-rel-cq/gn-args.json b/infra/config/generated/builders/ci/android-12l-x64-rel-cq/gn-args.json
new file mode 100644
index 0000000..ba021353
--- /dev/null
+++ b/infra/config/generated/builders/ci/android-12l-x64-rel-cq/gn-args.json
@@ -0,0 +1,21 @@
+{
+  "gn_args": {
+    "android_static_analysis": "off",
+    "dcheck_always_on": false,
+    "debuggable_apks": false,
+    "enable_chrome_android_internal": false,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "skip_secondary_abi_for_cq": true,
+    "strip_debug_info": true,
+    "symbol_level": 1,
+    "system_webview_package_name": "com.google.android.webview.debug",
+    "system_webview_shell_package_name": "org.chromium.my_webview_shell",
+    "target_cpu": "x64",
+    "target_os": "android",
+    "use_remoteexec": true,
+    "use_siso": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-12l-x64-rel-cq/properties.json b/infra/config/generated/builders/ci/android-12l-x64-rel-cq/properties.json
new file mode 100644
index 0000000..35b084a
--- /dev/null
+++ b/infra/config/generated/builders/ci/android-12l-x64-rel-cq/properties.json
@@ -0,0 +1,81 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/ci/android-12l-x64-rel-cq/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-12l-x64-rel-cq",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "x64_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 64,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-12l-x64-rel-cq",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "android-12l-x64-rel-cq",
+          "group": "tryserver.chromium.android"
+        }
+      ],
+      "targets_spec_directory": "src/infra/config/generated/builders/ci/android-12l-x64-rel-cq/targets"
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-trusted",
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-trusted",
+    "remote_jobs": 500
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.android",
+  "recipe": "chromium"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-12l-x64-rel-cq/shadow-properties.json b/infra/config/generated/builders/ci/android-12l-x64-rel-cq/shadow-properties.json
new file mode 100644
index 0000000..4325ef4
--- /dev/null
+++ b/infra/config/generated/builders/ci/android-12l-x64-rel-cq/shadow-properties.json
@@ -0,0 +1,17 @@
+{
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted",
+    "remote_jobs": 500
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-12l-x64-rel-cq/targets/chromium.android.json b/infra/config/generated/builders/ci/android-12l-x64-rel-cq/targets/chromium.android.json
new file mode 100644
index 0000000..2674697
--- /dev/null
+++ b/infra/config/generated/builders/ci/android-12l-x64-rel-cq/targets/chromium.android.json
@@ -0,0 +1,58 @@
+{
+  "android-12l-x64-rel-cq": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--annotation=Restriction=Tablet",
+          "--git-revision=${got_revision}",
+          "--avd-config=../../tools/android/avd/proto/android_32_google_apis_x64_foldable.textpb",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "tablet_sensitive_chrome_public_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "tablet_sensitive_chrome_public_test_apk",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "cores": "8",
+            "cpu": "x86-64",
+            "device_os": null,
+            "device_type": null,
+            "os": "Ubuntu-22.04",
+            "pool": "chromium.tests.avd"
+          },
+          "named_caches": [
+            {
+              "name": "android_32_google_apis_x64_foldable",
+              "path": ".android_emulator/android_32_google_apis_x64_foldable"
+            }
+          ],
+          "optional_dimensions": {
+            "60": {
+              "caches": "android_32_google_apis_x64_foldable"
+            }
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "chrome_public_test_apk",
+        "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json b/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
index 4d52d824..8760bc0 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
@@ -8,11 +8,11 @@
     "gtest_tests": [
       {
         "args": [
-          "--avd-config=../../tools/android/avd/proto/android_34_google_apis_x64.textpb",
+          "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
-        "description": "Run with android_34_google_apis_x64",
+        "description": "Run with android_34_desktop_x64",
         "merge": {
           "args": [
             "--bucket",
@@ -38,13 +38,13 @@
           },
           "named_caches": [
             {
-              "name": "android_34_google_apis_x64",
-              "path": ".android_emulator/android_34_google_apis_x64"
+              "name": "android_34_desktop_x64",
+              "path": ".android_emulator/android_34_desktop_x64"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_34_google_apis_x64"
+              "caches": "android_34_desktop_x64"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -55,11 +55,11 @@
       {
         "args": [
           "--git-revision=${got_revision}",
-          "--avd-config=../../tools/android/avd/proto/android_34_google_apis_x64.textpb",
+          "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
-        "description": "Run with android_34_google_apis_x64",
+        "description": "Run with android_34_desktop_x64",
         "merge": {
           "args": [
             "--bucket",
@@ -90,13 +90,13 @@
           },
           "named_caches": [
             {
-              "name": "android_34_google_apis_x64",
-              "path": ".android_emulator/android_34_google_apis_x64"
+              "name": "android_34_desktop_x64",
+              "path": ".android_emulator/android_34_desktop_x64"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_34_google_apis_x64"
+              "caches": "android_34_desktop_x64"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -107,11 +107,11 @@
       {
         "args": [
           "--git-revision=${got_revision}",
-          "--avd-config=../../tools/android/avd/proto/android_34_google_apis_x64.textpb",
+          "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
-        "description": "Run with android_34_google_apis_x64",
+        "description": "Run with android_34_desktop_x64",
         "merge": {
           "args": [
             "--bucket",
@@ -142,13 +142,13 @@
           },
           "named_caches": [
             {
-              "name": "android_34_google_apis_x64",
-              "path": ".android_emulator/android_34_google_apis_x64"
+              "name": "android_34_desktop_x64",
+              "path": ".android_emulator/android_34_desktop_x64"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_34_google_apis_x64"
+              "caches": "android_34_desktop_x64"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -160,9 +160,9 @@
     "isolated_scripts": [
       {
         "args": [
-          "--avd-config=../../tools/android/avd/proto/android_34_google_apis_x64.textpb"
+          "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb"
         ],
-        "description": "Run with android_34_google_apis_x64",
+        "description": "Run with android_34_desktop_x64",
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
@@ -182,13 +182,13 @@
           },
           "named_caches": [
             {
-              "name": "android_34_google_apis_x64",
-              "path": ".android_emulator/android_34_google_apis_x64"
+              "name": "android_34_desktop_x64",
+              "path": ".android_emulator/android_34_desktop_x64"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_34_google_apis_x64"
+              "caches": "android_34_desktop_x64"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-rel-14-tests/targets/chromium.android.desktop.json b/infra/config/generated/builders/ci/android-desktop-x64-rel-14-tests/targets/chromium.android.desktop.json
index 0afb901..5042f44 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-rel-14-tests/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-rel-14-tests/targets/chromium.android.desktop.json
@@ -3,11 +3,11 @@
     "gtest_tests": [
       {
         "args": [
-          "--avd-config=../../tools/android/avd/proto/android_34_google_apis_x64.textpb",
+          "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
-        "description": "Run with android_34_google_apis_x64",
+        "description": "Run with android_34_desktop_x64",
         "merge": {
           "args": [
             "--bucket",
@@ -33,13 +33,13 @@
           },
           "named_caches": [
             {
-              "name": "android_34_google_apis_x64",
-              "path": ".android_emulator/android_34_google_apis_x64"
+              "name": "android_34_desktop_x64",
+              "path": ".android_emulator/android_34_desktop_x64"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_34_google_apis_x64"
+              "caches": "android_34_desktop_x64"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -50,11 +50,11 @@
       {
         "args": [
           "--git-revision=${got_revision}",
-          "--avd-config=../../tools/android/avd/proto/android_34_google_apis_x64.textpb",
+          "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
-        "description": "Run with android_34_google_apis_x64",
+        "description": "Run with android_34_desktop_x64",
         "merge": {
           "args": [
             "--bucket",
@@ -85,13 +85,13 @@
           },
           "named_caches": [
             {
-              "name": "android_34_google_apis_x64",
-              "path": ".android_emulator/android_34_google_apis_x64"
+              "name": "android_34_desktop_x64",
+              "path": ".android_emulator/android_34_desktop_x64"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_34_google_apis_x64"
+              "caches": "android_34_desktop_x64"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -102,11 +102,11 @@
       {
         "args": [
           "--git-revision=${got_revision}",
-          "--avd-config=../../tools/android/avd/proto/android_34_google_apis_x64.textpb",
+          "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
-        "description": "Run with android_34_google_apis_x64",
+        "description": "Run with android_34_desktop_x64",
         "merge": {
           "args": [
             "--bucket",
@@ -137,13 +137,13 @@
           },
           "named_caches": [
             {
-              "name": "android_34_google_apis_x64",
-              "path": ".android_emulator/android_34_google_apis_x64"
+              "name": "android_34_desktop_x64",
+              "path": ".android_emulator/android_34_desktop_x64"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_34_google_apis_x64"
+              "caches": "android_34_desktop_x64"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -155,9 +155,9 @@
     "isolated_scripts": [
       {
         "args": [
-          "--avd-config=../../tools/android/avd/proto/android_34_google_apis_x64.textpb"
+          "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb"
         ],
-        "description": "Run with android_34_google_apis_x64",
+        "description": "Run with android_34_desktop_x64",
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
@@ -177,13 +177,13 @@
           },
           "named_caches": [
             {
-              "name": "android_34_google_apis_x64",
-              "path": ".android_emulator/android_34_google_apis_x64"
+              "name": "android_34_desktop_x64",
+              "path": ".android_emulator/android_34_desktop_x64"
             }
           ],
           "optional_dimensions": {
             "60": {
-              "caches": "android_34_google_apis_x64"
+              "caches": "android_34_desktop_x64"
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index 37fae95d..ad8d33d 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -38,6 +38,7 @@
     "android-10-arm64-rel": "ci/android-10-arm64-rel/gn-args.json",
     "android-11-x86-rel": "ci/android-11-x86-rel/gn-args.json",
     "android-12-x64-rel": "ci/android-12-x64-rel/gn-args.json",
+    "android-12l-x64-rel-cq": "ci/android-12l-x64-rel-cq/gn-args.json",
     "android-13-x64-rel": "ci/android-13-x64-rel/gn-args.json",
     "android-14-arm64-rel": "ci/android-14-arm64-rel/gn-args.json",
     "android-14-x64-rel": "ci/android-14-x64-rel/gn-args.json",
@@ -558,6 +559,7 @@
     "android-12-x64-rel": "try/android-12-x64-rel/gn-args.json",
     "android-12l-landscape-x64-dbg": "try/android-12l-landscape-x64-dbg/gn-args.json",
     "android-12l-x64-dbg": "try/android-12l-x64-dbg/gn-args.json",
+    "android-12l-x64-rel-cq": "try/android-12l-x64-rel-cq/gn-args.json",
     "android-13-x64-rel": "try/android-13-x64-rel/gn-args.json",
     "android-14-arm64-fyi-rel": "try/android-14-arm64-fyi-rel/gn-args.json",
     "android-14-arm64-rel": "try/android-14-arm64-rel/gn-args.json",
diff --git a/infra/config/generated/builders/try/android-12l-x64-rel-cq/gn-args.json b/infra/config/generated/builders/try/android-12l-x64-rel-cq/gn-args.json
new file mode 100644
index 0000000..2ecb2fd
--- /dev/null
+++ b/infra/config/generated/builders/try/android-12l-x64-rel-cq/gn-args.json
@@ -0,0 +1,21 @@
+{
+  "gn_args": {
+    "android_static_analysis": "off",
+    "dcheck_always_on": true,
+    "debuggable_apks": false,
+    "enable_chrome_android_internal": false,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "skip_secondary_abi_for_cq": true,
+    "strip_debug_info": true,
+    "symbol_level": 0,
+    "system_webview_package_name": "com.google.android.webview.debug",
+    "system_webview_shell_package_name": "org.chromium.my_webview_shell",
+    "target_cpu": "x64",
+    "target_os": "android",
+    "use_remoteexec": true,
+    "use_siso": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-12l-x64-rel-cq/properties.json b/infra/config/generated/builders/try/android-12l-x64-rel-cq/properties.json
new file mode 100644
index 0000000..6b70e4564
--- /dev/null
+++ b/infra/config/generated/builders/try/android-12l-x64-rel-cq/properties.json
@@ -0,0 +1,75 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/android-12l-x64-rel-cq/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-12l-x64-rel-cq",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "x64_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 64,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-12l-x64-rel-cq",
+          "project": "chromium"
+        }
+      ],
+      "targets_spec_directory": "src/infra/config/generated/builders/try/android-12l-x64-rel-cq/targets"
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted",
+    "remote_jobs": 150
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.android",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-12l-x64-rel-cq/targets/chromium.android.json b/infra/config/generated/builders/try/android-12l-x64-rel-cq/targets/chromium.android.json
new file mode 100644
index 0000000..2674697
--- /dev/null
+++ b/infra/config/generated/builders/try/android-12l-x64-rel-cq/targets/chromium.android.json
@@ -0,0 +1,58 @@
+{
+  "android-12l-x64-rel-cq": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--annotation=Restriction=Tablet",
+          "--git-revision=${got_revision}",
+          "--avd-config=../../tools/android/avd/proto/android_32_google_apis_x64_foldable.textpb",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "tablet_sensitive_chrome_public_test_apk"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
+        },
+        "name": "tablet_sensitive_chrome_public_test_apk",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "cores": "8",
+            "cpu": "x86-64",
+            "device_os": null,
+            "device_type": null,
+            "os": "Ubuntu-22.04",
+            "pool": "chromium.tests.avd"
+          },
+          "named_caches": [
+            {
+              "name": "android_32_google_apis_x64_foldable",
+              "path": ".android_emulator/android_32_google_apis_x64_foldable"
+            }
+          ],
+          "optional_dimensions": {
+            "60": {
+              "caches": "android_32_google_apis_x64_foldable"
+            }
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "chrome_public_test_apk",
+        "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux_chromium_compile_dbg_ng/targets/chromium.linux.json b/infra/config/generated/builders/try/linux_chromium_compile_dbg_ng/targets/chromium.linux.json
index 1adf256..34321f0 100644
--- a/infra/config/generated/builders/try/linux_chromium_compile_dbg_ng/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux_chromium_compile_dbg_ng/targets/chromium.linux.json
@@ -1261,7 +1261,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 15
         },
         "test": "blink_web_tests",
         "test_id_prefix": "ninja://:blink_web_tests/"
diff --git a/infra/config/generated/builders/try/linux_chromium_dbg_ng/targets/chromium.linux.json b/infra/config/generated/builders/try/linux_chromium_dbg_ng/targets/chromium.linux.json
index 1adf256..34321f0 100644
--- a/infra/config/generated/builders/try/linux_chromium_dbg_ng/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux_chromium_dbg_ng/targets/chromium.linux.json
@@ -1261,7 +1261,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
+          "shards": 15
         },
         "test": "blink_web_tests",
         "test_id_prefix": "ninja://:blink_web_tests/"
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index 6f6c903..9f8ab1dc 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -6619,6 +6619,27 @@
           }
         ]
       },
+      "android-12l-x64-rel-cq": {
+        "contact_team_email": "clank-engprod@google.com",
+        "problem_specs": [
+          {
+            "name": "Unhealthy",
+            "period_days": 7,
+            "score": 5,
+            "thresholds": {
+              "_default": "_default"
+            }
+          },
+          {
+            "name": "Low Value",
+            "period_days": 90,
+            "score": 1,
+            "thresholds": {
+              "_default": "_default"
+            }
+          }
+        ]
+      },
       "android-13-x64-fyi-rel": {
         "problem_specs": [
           {
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 22b84f4..322f2a8 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -690,6 +690,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/android-12l-x64-rel-cq"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/android-13-x64-rel"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 8ec9a42..db6319f0 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -32206,7 +32206,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "Run Chromium tests on Android 12l emulator in Landscape Mode.<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android-12l-landscape-x64-dbg\">android-12l-landscape-x64-dbg</a></li></ul>"
+      description_html: "Run Chromium tests on Android 12l tablet-flavor emulator in Landscape Mode.<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android-12l-landscape-x64-dbg\">android-12l-landscape-x64-dbg</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -32305,7 +32305,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android-12l-x64-dbg\">android-12l-x64-dbg</a></li></ul>"
+      description_html: "Run Chromium tests on Android 12l tablet-flavor emulator.<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android-12l-x64-dbg\">android-12l-x64-dbg</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -32407,6 +32407,99 @@
       }
     }
     builders {
+      name: "android-12l-x64-rel-cq"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/android-12l-x64-rel-cq/properties.json",'
+        '    "shadow_properties_file": "infra/config/generated/builders/ci/android-12l-x64-rel-cq/shadow-properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.android",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "Run CQ-specific Chromium test suites on Android 12l tablet-flavor emulator.<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android-12l-x64-rel-cq\">android-12l-x64-rel-cq</a></li></ul>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+        pool: "luci.chromium.try"
+        dimensions: "free_space:"
+        dimensions: "pool:luci.chromium.try"
+      }
+      contact_team_email: "clank-engprod@google.com"
+    }
+    builders {
       name: "android-13-x64-fyi-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -67957,6 +68050,103 @@
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android x64 Builder (dbg)\">Android x64 Builder (dbg)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/android-12l-x64-dbg-tests\">android-12l-x64-dbg-tests</a></li></ul>"
     }
     builders {
+      name: "android-12l-x64-rel-cq"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/android-12l-x64-rel-cq/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.android",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.buildbucket.canary_software"
+        value: 5
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "swarming.prpc.cli"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "<br>Run CQ-specific Chromium test suites on Android 12l tablet-flavor emulator.<br/><br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/android-12l-x64-rel-cq\">android-12l-x64-rel-cq</a></li></ul>"
+      contact_team_email: "clank-engprod@google.com"
+    }
+    builders {
       name: "android-13-x64-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 39be627a..7d0447c9 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -5051,6 +5051,11 @@
     short_name: "12"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/android-12l-x64-rel-cq"
+    category: "builder_tester|x64"
+    short_name: "12L"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/android-14-x64-rel"
     category: "builder_tester|x64"
     short_name: "14"
@@ -16903,6 +16908,9 @@
     name: "buildbucket/luci.chromium.try/android-12l-x64-dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-12l-x64-rel-cq"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-13-x64-rel"
   }
   builders {
@@ -18375,6 +18383,9 @@
     name: "buildbucket/luci.chromium.try/android-12l-x64-dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-12l-x64-rel-cq"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-13-x64-rel"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 8fa91576..bc648bf 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -3375,6 +3375,15 @@
   }
 }
 job {
+  id: "android-12l-x64-rel-cq"
+  realm: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "android-12l-x64-rel-cq"
+  }
+}
+job {
   id: "android-13-x64-fyi-rel"
   realm: "ci"
   buildbucket {
@@ -6687,6 +6696,7 @@
   triggers: "android-11-x86-rel"
   triggers: "android-12-x64-rel"
   triggers: "android-12l-x64-fyi-dbg"
+  triggers: "android-12l-x64-rel-cq"
   triggers: "android-13-x64-fyi-rel"
   triggers: "android-13-x64-rel"
   triggers: "android-14-arm64-fyi-rel"
diff --git a/infra/config/generated/testing/gn_isolate_map.pyl b/infra/config/generated/testing/gn_isolate_map.pyl
index 8443b1f..7ea5de0 100644
--- a/infra/config/generated/testing/gn_isolate_map.pyl
+++ b/infra/config/generated/testing/gn_isolate_map.pyl
@@ -958,10 +958,6 @@
     "label": "//ios/net:ios_net_unittests",
     "type": "generated_script",
   },
-  "ios_remoting_unittests": {
-    "label": "//remoting/ios:ios_remoting_unittests",
-    "type": "generated_script",
-  },
   "ios_testing_unittests": {
     "label": "//ios/testing:ios_testing_unittests",
     "type": "generated_script",
diff --git a/infra/config/generated/testing/test_suites.pyl b/infra/config/generated/testing/test_suites.pyl
index d6e835c..15a3175e 100644
--- a/infra/config/generated/testing/test_suites.pyl
+++ b/infra/config/generated/testing/test_suites.pyl
@@ -4167,7 +4167,6 @@
           'shards': 3,
         },
       },
-      'ios_remoting_unittests': {},
       'ios_testing_unittests': {},
       'net_unittests': {},
       'services_unittests': {},
@@ -4226,10 +4225,6 @@
       },
     },
 
-    'ios_remoting_fyi_unittests': {
-      'ios_remoting_unittests': {},
-    },
-
     'ios_screen_size_dependent_tests': {
       'base_unittests': {},
       'components_unittests': {},
@@ -7709,17 +7704,6 @@
       },
     },
 
-    'ios_webrtc_fyi_tests': {
-      'ios_remoting_fyi_unittests': {
-        'variants': [
-          'SIM_IPAD_AIR_5TH_GEN_17_5',
-          'SIM_IPAD_AIR_6TH_GEN_18_0',
-          'SIM_IPHONE_14_17_5',
-          'SIM_IPHONE_15_18_0',
-        ],
-      },
-    },
-
     'linux_optional_gpu_tests_rel_gpu_telemetry_tests': {
       'gpu_common_and_optional_telemetry_tests': {
         'variants': [
diff --git a/infra/config/lib/builder_exemptions.star b/infra/config/lib/builder_exemptions.star
index 6f39c8b..9fc7b37 100644
--- a/infra/config/lib/builder_exemptions.star
+++ b/infra/config/lib/builder_exemptions.star
@@ -268,7 +268,6 @@
         "android-11-x86-rel",
         "android-12-x64-dbg-tests",
         "android-12-x64-fyi-rel",
-        "android-12l-x64-dbg-tests",
         "android-12l-x64-fyi-dbg",
         "android-13-x64-fyi-rel",
         "android-androidx-packager",
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.desktop.star b/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
index 1218049..a5df3bb 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.desktop.star
@@ -253,8 +253,7 @@
     targets = targets.bundle(
         targets = "android_desktop_tests",
         mixins = [
-            # TODO(b/362336776): Switch to Android Desktop AVD when ready.
-            "14-x64-emulator",
+            "14-desktop-x64-emulator",
             "emulator-8-cores",
             "has_native_resultdb_integration",
             "linux-jammy",
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star
index b053aa4..18bd550d 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -793,6 +793,7 @@
 
 ci.builder(
     name = "android-12l-x64-dbg-tests",
+    description_html = "Run Chromium tests on Android 12l tablet-flavor emulator.",
     triggered_by = ["ci/Android x64 Builder (dbg)"],
     builder_spec = builder_config.builder_spec(
         execution_mode = builder_config.execution_mode.TEST,
@@ -825,7 +826,8 @@
 
 ci.builder(
     name = "android-12l-landscape-x64-dbg-tests",
-    description_html = "Run Chromium tests on Android 12l emulator in Landscape Mode.",
+    description_html = "Run Chromium tests on Android 12l tablet-flavor " +
+                       "emulator in Landscape Mode.",
     triggered_by = ["ci/Android x64 Builder (dbg)"],
     builder_spec = builder_config.builder_spec(
         execution_mode = builder_config.execution_mode.TEST,
@@ -857,6 +859,36 @@
 )
 
 ci.builder(
+    name = "android-12l-x64-rel-cq",
+    # TODO(crbug.com/364967534): Enable on branch once stable.
+    # branch_selector = branches.selector.ANDROID_BRANCHES,
+    description_html = "Run CQ-specific Chromium test suites on Android 12l " +
+                       "tablet-flavor emulator.",
+    builder_spec = builder_config.copy_from("ci/android-13-x64-rel"),
+    gn_args = "ci/android-13-x64-rel",
+    targets = targets.bundle(
+        targets = "android_12l_rel_cq_gtests",
+        mixins = [
+            "12l-x64-emulator",
+            "emulator-8-cores",
+            "has_native_resultdb_integration",
+            "linux-jammy",
+            "x86-64",
+        ],
+    ),
+    # TODO(crbug.com/364967534): Enable gardener_rotations once stable.
+    gardener_rotations = args.ignore_default(None),
+    # TODO(crbug.com/364967534): Enable tree_closing once stable.
+    tree_closing = False,
+    console_view_entry = consoles.console_view_entry(
+        # TODO(crbug.com/364967534): Change to "on_cq".
+        category = "builder_tester|x64",
+        short_name = "12L",
+    ),
+    contact_team_email = "clank-engprod@google.com",
+)
+
+ci.builder(
     name = "android-arm64-proguard-rel",
     builder_spec = builder_config.builder_spec(
         gclient_config = builder_config.gclient_config(
diff --git a/infra/config/subprojects/chromium/ci/chromium.linux.star b/infra/config/subprojects/chromium/ci/chromium.linux.star
index db3f5ba..3e6e543f 100644
--- a/infra/config/subprojects/chromium/ci/chromium.linux.star
+++ b/infra/config/subprojects/chromium/ci/chromium.linux.star
@@ -330,7 +330,7 @@
         per_test_modifications = {
             "blink_web_tests": targets.mixin(
                 swarming = targets.swarming(
-                    shards = 12,
+                    shards = 15,
                 ),
             ),
             "blink_wpt_tests": targets.mixin(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 10affbed..7febd36d 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -120,6 +120,23 @@
 )
 
 try_.builder(
+    name = "android-12l-x64-rel-cq",
+    # TODO(crbug.com/364967534): Enable on branch once stable.
+    # branch_selector = branches.selector.ANDROID_BRANCHES,
+    mirrors = [
+        "ci/android-12l-x64-rel-cq",
+    ],
+    gn_args = gn_args.config(
+        configs = [
+            "ci/android-12l-x64-rel-cq",
+            "release_try_builder",
+        ],
+    ),
+    contact_team_email = "clank-engprod@google.com",
+    siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
+)
+
+try_.builder(
     name = "android-13-x64-rel",
     branch_selector = branches.selector.ANDROID_BRANCHES,
     mirrors = [
diff --git a/infra/config/targets/basic_suites.star b/infra/config/targets/basic_suites.star
index 8de878b..924f0804 100644
--- a/infra/config/targets/basic_suites.star
+++ b/infra/config/targets/basic_suites.star
@@ -3788,7 +3788,6 @@
                 shards = 3,
             ),
         ),
-        "ios_remoting_unittests": targets.legacy_test_config(),
         "ios_testing_unittests": targets.legacy_test_config(),
         "net_unittests": targets.legacy_test_config(),
         # TODO(https://bugs.chromium.org/p/gn/issues/detail?id=340): Enable this.
@@ -3860,13 +3859,6 @@
 )
 
 targets.legacy_basic_suite(
-    name = "ios_remoting_fyi_unittests",
-    tests = {
-        "ios_remoting_unittests": targets.legacy_test_config(),
-    },
-)
-
-targets.legacy_basic_suite(
     name = "ios_screen_size_dependent_tests",
     tests = {
         "base_unittests": targets.legacy_test_config(),
diff --git a/infra/config/targets/binaries.star b/infra/config/targets/binaries.star
index 9867cec3..e8f29c50 100644
--- a/infra/config/targets/binaries.star
+++ b/infra/config/targets/binaries.star
@@ -1005,11 +1005,6 @@
 )
 
 targets.binaries.generated_script(
-    name = "ios_remoting_unittests",
-    label = "//remoting/ios:ios_remoting_unittests",
-)
-
-targets.binaries.generated_script(
     name = "ios_testing_unittests",
     label = "//ios/testing:ios_testing_unittests",
 )
diff --git a/infra/config/targets/bundles.star b/infra/config/targets/bundles.star
index 412de40..1c09a50 100644
--- a/infra/config/targets/bundles.star
+++ b/infra/config/targets/bundles.star
@@ -18,6 +18,13 @@
 )
 
 targets.bundle(
+    name = "android_12l_rel_cq_gtests",
+    targets = [
+        "tablet_sensitive_chrome_public_test_apk",
+    ],
+)
+
+targets.bundle(
     name = "android_ar_gtests",
     targets = [
         "monochrome_public_test_ar_apk",
diff --git a/infra/config/targets/matrix_compound_suites.star b/infra/config/targets/matrix_compound_suites.star
index c1f0471..5d26102 100644
--- a/infra/config/targets/matrix_compound_suites.star
+++ b/infra/config/targets/matrix_compound_suites.star
@@ -1328,20 +1328,6 @@
 )
 
 targets.legacy_matrix_compound_suite(
-    name = "ios_webrtc_fyi_tests",
-    basic_suites = {
-        "ios_remoting_fyi_unittests": targets.legacy_matrix_config(
-            variants = [
-                "SIM_IPHONE_14_17_5",
-                "SIM_IPAD_AIR_5TH_GEN_17_5",
-                "SIM_IPHONE_15_18_0",
-                "SIM_IPAD_AIR_6TH_GEN_18_0",
-            ],
-        ),
-    },
-)
-
-targets.legacy_matrix_compound_suite(
     name = "linux_optional_gpu_tests_rel_gpu_telemetry_tests",
     basic_suites = {
         "gpu_common_and_optional_telemetry_tests": targets.legacy_matrix_config(
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index e751960..39609f6c 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -240,6 +240,29 @@
 )
 
 targets.mixin(
+    name = "14-desktop-x64-emulator",
+    description = "Run with android_34_desktop_x64",
+    generate_pyl_entry = False,
+    args = [
+        "--avd-config=../../tools/android/avd/proto/android_34_desktop_x64.textpb",
+    ],
+    swarming = targets.swarming(
+        # soft affinity so that bots with caches will be picked first
+        optional_dimensions = {
+            60: {
+                "caches": "android_34_desktop_x64",
+            },
+        },
+        named_caches = [
+            swarming.cache(
+                name = "android_34_desktop_x64",
+                path = ".android_emulator/android_34_desktop_x64",
+            ),
+        ],
+    ),
+)
+
+targets.mixin(
     name = "15-x64-emulator",
     description = "Run with android_35_google_apis_x64",
     args = [
diff --git a/infra/config/targets/tests.star b/infra/config/targets/tests.star
index 6cb0cce5..e8d5759 100644
--- a/infra/config/targets/tests.star
+++ b/infra/config/targets/tests.star
@@ -1388,10 +1388,6 @@
 )
 
 targets.tests.isolated_script_test(
-    name = "ios_remoting_unittests",
-)
-
-targets.tests.isolated_script_test(
     name = "ios_testing_unittests",
 )
 
@@ -2136,6 +2132,17 @@
     binary = "browser_tests",
 )
 
+targets.tests.gtest_test(
+    name = "tablet_sensitive_chrome_public_test_apk",
+    mixins = [
+        "skia_gold_test",
+    ],
+    args = [
+        "--annotation=Restriction=Tablet",
+    ],
+    binary = "chrome_public_test_apk",
+)
+
 targets.tests.isolated_script_test(
     name = "telemetry_chromium_minidump_unittests",
     args = [
diff --git a/internal b/internal
index bcd1edc..93d8f1d 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit bcd1edce7de23dda9e50fd7bf840b5438975b942
+Subproject commit 93d8f1d5b956eb8dc7857ad3083c8afcdb8ab288
diff --git a/ios/build/bots/tests/common_tests.json b/ios/build/bots/tests/common_tests.json
index ad8a2931..4b75e8c 100644
--- a/ios/build/bots/tests/common_tests.json
+++ b/ios/build/bots/tests/common_tests.json
@@ -19,9 +19,6 @@
       "app": "ios_net_unittests"
     },
     {
-      "app": "ios_remoting_unittests"
-    },
-    {
       "app": "net_unittests"
     },
     {
diff --git a/ios/chrome/browser/context_menu/ui_bundled/BUILD.gn b/ios/chrome/browser/context_menu/ui_bundled/BUILD.gn
index f1742a9..1beb42da 100644
--- a/ios/chrome/browser/context_menu/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/context_menu/ui_bundled/BUILD.gn
@@ -7,6 +7,7 @@
     "context_menu_configuration_provider+Testing.h",
     "context_menu_configuration_provider.h",
     "context_menu_configuration_provider.mm",
+    "context_menu_configuration_provider_delegate.h",
   ]
   deps = [
     ":ui",
diff --git a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.h b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.h
index 563f55a..c4363e5f 100644
--- a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.h
+++ b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.h
@@ -13,6 +13,7 @@
 }
 
 class Browser;
+@protocol ContextMenuConfigurationProviderDelegate;
 class GURL;
 
 // Object creating the configuration (action items...) for the context menu.
@@ -48,6 +49,10 @@
 // nothing to load.
 @property(nonatomic, assign, readonly) GURL URLToLoad;
 
+// Delegate for events in this class.
+@property(nonatomic, weak) id<ContextMenuConfigurationProviderDelegate>
+    delegate;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_CONTEXT_MENU_UI_BUNDLED_CONTEXT_MENU_CONFIGURATION_PROVIDER_H_
diff --git a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
index fdc036c..431158a 100644
--- a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
+++ b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
@@ -12,6 +12,7 @@
 #import "components/prefs/pref_service.h"
 #import "components/search_engines/template_url_service.h"
 #import "ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider+Testing.h"
+#import "ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider_delegate.h"
 #import "ios/chrome/browser/context_menu/ui_bundled/context_menu_utils.h"
 #import "ios/chrome/browser/context_menu/ui_bundled/image_preview_view_controller.h"
 #import "ios/chrome/browser/favicon/model/favicon_loader.h"
@@ -355,6 +356,7 @@
         return;
       }
       UrlLoadingBrowserAgent::FromBrowser(strongSelf.browser)->Load(loadParams);
+      [strongSelf didOpenTabInBackground:linkURL];
     }];
     [linkOpeningElements addObject:openNewTab];
 
@@ -603,6 +605,7 @@
 
     UrlLoadingBrowserAgent::FromBrowser(strongSelf.browser)
         ->Load(groupLoadParams);
+    [strongSelf didOpenTabInBackground:linkURL];
   };
 
   return [actionFactory menuToOpenLinkInGroupWithGroups:groups
@@ -641,9 +644,13 @@
   loadParams.origin_point = [params.view convertPoint:params.location
                                                toView:nil];
 
-  UIAction* openImageInNewTab =
-      [actionFactory actionOpenImageInNewTabWithUrlLoadParams:loadParams
-                                                   completion:nil];
+  __weak __typeof__(self) weakSelf = self;
+  UIAction* openImageInNewTab = [actionFactory
+      actionOpenImageInNewTabWithUrlLoadParams:loadParams
+                                    completion:^() {
+                                      [weakSelf
+                                          didOpenTabInBackground:imageURL];
+                                    }];
 
   // Check if the URL was a valid link to avoid having the `Open in Tab Group`
   // option twice.
@@ -840,4 +847,10 @@
   [handler shareURLFromContextMenu:command];
 }
 
+// Informs the delegate that a new tab has been opened in the background.
+- (void)didOpenTabInBackground:(GURL)URL {
+  [self.delegate contextMenuConfigurationProvider:self
+                 didOpenNewTabInBackgroundWithURL:URL];
+}
+
 @end
diff --git a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider_delegate.h b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider_delegate.h
new file mode 100644
index 0000000..f4d692c
--- /dev/null
+++ b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider_delegate.h
@@ -0,0 +1,21 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_CONTEXT_MENU_UI_BUNDLED_CONTEXT_MENU_CONFIGURATION_PROVIDER_DELEGATE_H_
+#define IOS_CHROME_BROWSER_CONTEXT_MENU_UI_BUNDLED_CONTEXT_MENU_CONFIGURATION_PROVIDER_DELEGATE_H_
+
+@class ContextMenuConfigurationProvider;
+class GURL;
+
+/// Delegate for events in ContextMenuConfigurationProvider.
+@protocol ContextMenuConfigurationProviderDelegate <NSObject>
+
+/// Called when the context menu did open a new tab in the background.
+- (void)contextMenuConfigurationProvider:
+            (ContextMenuConfigurationProvider*)configurationProvider
+        didOpenNewTabInBackgroundWithURL:(GURL)URL;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_CONTEXT_MENU_UI_BUNDLED_CONTEXT_MENU_CONFIGURATION_PROVIDER_DELEGATE_H_
diff --git a/ios/chrome/browser/contextual_panel/entrypoint/coordinator/contextual_panel_entrypoint_mediator.mm b/ios/chrome/browser/contextual_panel/entrypoint/coordinator/contextual_panel_entrypoint_mediator.mm
index f97b444..271367c 100644
--- a/ios/chrome/browser/contextual_panel/entrypoint/coordinator/contextual_panel_entrypoint_mediator.mm
+++ b/ios/chrome/browser/contextual_panel/entrypoint/coordinator/contextual_panel_entrypoint_mediator.mm
@@ -242,8 +242,12 @@
 - (void)infobarBadgesUpdated:(InfobarBadgeTabHelper*)tabHelper {
   // Return early if the notification doesn't come from the currently active
   // webstate's tab helper.
-  if (tabHelper != InfobarBadgeTabHelper::GetOrCreateForWebState(
-                       _webStateList->GetActiveWebState())) {
+  raw_ptr<web::WebState> active_web_state = _webStateList->GetActiveWebState();
+  if (!active_web_state || active_web_state->IsBeingDestroyed()) {
+    return;
+  }
+  if (tabHelper !=
+      InfobarBadgeTabHelper::GetOrCreateForWebState(active_web_state)) {
     return;
   }
 
diff --git a/ios/chrome/browser/contextual_panel/ui/panel_content_view_controller.mm b/ios/chrome/browser/contextual_panel/ui/panel_content_view_controller.mm
index 2ecf0a5..5ef70b2 100644
--- a/ios/chrome/browser/contextual_panel/ui/panel_content_view_controller.mm
+++ b/ios/chrome/browser/contextual_panel/ui/panel_content_view_controller.mm
@@ -58,6 +58,12 @@
 // Threshold for how long a view is onscreen to count as visible.
 const base::TimeDelta kVisibleTimeThreshold = base::Milliseconds(10);
 
+// The time range's expected min, max values and bucket count for custom
+// histograms.
+constexpr base::TimeDelta kVisibleTimeHistogramMin = base::Milliseconds(1);
+constexpr base::TimeDelta kVisibleTimeHistogramMax = base::Minutes(10);
+constexpr int kVisibleTimeHistogramBucketCount = 100;
+
 // Identifier for the one section in this collection view.
 NSString* const kSectionIdentifier = @"section1";
 
@@ -259,8 +265,10 @@
 - (void)viewWillDisappear:(BOOL)animated {
   [super viewWillDisappear:animated];
 
-  base::UmaHistogramTimes("IOS.ContextualPanel.VisibleTime",
-                          base::Time::Now() - _appearanceTime);
+  base::UmaHistogramCustomTimes(
+      "IOS.ContextualPanel.VisibleTime", base::Time::Now() - _appearanceTime,
+      kVisibleTimeHistogramMin, kVisibleTimeHistogramMax,
+      kVisibleTimeHistogramBucketCount);
 
   // First alert all visible cells that they will disappear.
   for (NSIndexPath* indexPath in _collectionView.indexPathsForVisibleItems) {
diff --git a/ios/chrome/browser/ntp/ui_bundled/BUILD.gn b/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
index e10245a1..b044663a 100644
--- a/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
@@ -154,6 +154,7 @@
     "//ios/chrome/browser/ui/content_suggestions/cells:constants",
     "//ios/chrome/browser/ui/sharing",
     "//ios/chrome/browser/ui/toolbar/public",
+    "//ios/chrome/browser/ui/toolbar/tab_groups/coordinator",
     "//ios/chrome/browser/url_loading/model",
     "//ios/chrome/common:timing",
     "//ios/chrome/common/ui/util",
@@ -246,6 +247,8 @@
     "//ios/chrome/browser/ui/toolbar/buttons",
     "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/toolbar/public:constants",
+    "//ios/chrome/browser/ui/toolbar/tab_groups/ui",
+    "//ios/chrome/browser/ui/toolbar/tab_groups/ui:constants",
     "//ios/chrome/browser/url_loading/model",
     "//ios/chrome/common:timing",
     "//ios/chrome/common/ui/colors",
diff --git a/ios/chrome/browser/ntp/ui_bundled/DEPS b/ios/chrome/browser/ntp/ui_bundled/DEPS
index 6e1674b..8307d6a 100644
--- a/ios/chrome/browser/ntp/ui_bundled/DEPS
+++ b/ios/chrome/browser/ntp/ui_bundled/DEPS
@@ -55,6 +55,9 @@
   "+ios/chrome/browser/sync/model/sync_service_factory.h",
   "+ios/chrome/browser/url_loading/model/url_loading_browser_agent.h",
   "+ios/chrome/browser/url_loading/model/url_loading_params.h",
+  "+ios/chrome/browser/ui/toolbar/tab_groups/coordinator/tab_group_indicator_coordinator.h",
+  "+ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.h",
+  "+ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_view.h",
 ]
 
 specific_include_rules = {
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
index 196b29b..d19b632 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
@@ -118,6 +118,7 @@
 #import "ios/chrome/browser/ui/sharing/sharing_coordinator.h"
 #import "ios/chrome/browser/ui/sharing/sharing_params.h"
 #import "ios/chrome/browser/ui/toolbar/public/fakebox_focuser.h"
+#import "ios/chrome/browser/ui/toolbar/tab_groups/coordinator/tab_group_indicator_coordinator.h"
 #import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
 #import "ios/chrome/common/material_timing.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
@@ -278,6 +279,8 @@
   AccountMenuCoordinator* _accountMenuCoordinator;
   // Coordinator for presenting the Home customization menu.
   HomeCustomizationCoordinator* _customizationCoordinator;
+  // Coordinator for the tab group indicator.
+  TabGroupIndicatorCoordinator* _tabGroupIndicatorCoordinator;
 }
 
 // Synthesize NewTabPageConfiguring properties.
@@ -371,6 +374,9 @@
   [self configureContentSuggestionsCoordinator];
   [self configureFeedMetricsRecorder];
   [self configureNTPViewController];
+  if (IsTabGroupIndicatorEnabled()) {
+    [self configureTabGroupIndicator];
+  }
 
   self.started = YES;
 }
@@ -396,6 +402,11 @@
 
   [sceneState.appState removeObserver:self];
 
+  if (IsTabGroupIndicatorEnabled()) {
+    [_tabGroupIndicatorCoordinator stop];
+    _tabGroupIndicatorCoordinator = nil;
+  }
+
   [self.feedManagementCoordinator stop];
   self.feedManagementCoordinator = nil;
   [self.contentSuggestionsCoordinator stop];
@@ -834,6 +845,20 @@
   self.NTPViewController.mutator = self.NTPMediator;
 }
 
+// Configures the `_tabGroupIndicatorCoordinator` and sets the
+// `tabGroupIndicatorView` to the `headerViewController`.
+- (void)configureTabGroupIndicator {
+  // The `_tabGroupIndicatorCoordinator` should be configured after the
+  // `AdaptiveToolbarCoordinator` to gain access to the `NTPViewController`.
+  _tabGroupIndicatorCoordinator = [[TabGroupIndicatorCoordinator alloc]
+      initWithBaseViewController:self.NTPViewController
+                         browser:self.browser];
+  _tabGroupIndicatorCoordinator.toolbarHeightDelegate = nil;
+  [_tabGroupIndicatorCoordinator start];
+  [self.headerViewController
+      setTabGroupIndicatorView:_tabGroupIndicatorCoordinator.view];
+}
+
 // Configures the main ViewController managed by this Coordinator.
 - (void)configureMainViewControllerUsing:
     (UIViewController*)containedViewController {
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.h b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.h
index 0c5e7f05c..a96a62d 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.h
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.h
@@ -8,6 +8,7 @@
 #import <UIKit/UIKit.h>
 
 @class GradientView;
+@class TabGroupIndicatorView;
 
 // Header view for the NTP. The header view contains all views that are
 // displayed above the list of most visited sites, which includes the
@@ -47,6 +48,9 @@
 @property(nonatomic, strong) GradientView* fakeLocationBar;
 @property(nonatomic, strong) UILabel* searchHintLabel;
 
+// View that contains tab group information.
+@property(nonatomic, weak) TabGroupIndicatorView* tabGroupIndicatorView;
+
 // `YES` if Google is the default search engine.
 @property(nonatomic, assign) BOOL isGoogleDefaultSearchEngine;
 
@@ -107,6 +111,10 @@
 // Hides the new feature badge on the Home customization menu's entrypoint.
 - (void)hideBadgeOnCustomizationMenu;
 
+// Updates the `tabGroupIndicatorView` availability.
+// `offset` represents the scroll view's y `offset`.
+- (void)updateTabGroupIndicatorAvailabilityWithOffset:(CGFloat)offset;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_NTP_UI_BUNDLED_NEW_TAB_PAGE_HEADER_VIEW_H_
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
index aff3029e..2046bec 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
@@ -12,6 +12,10 @@
 #import "base/feature_list.h"
 #import "components/prefs/pref_service.h"
 #import "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_delegate.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/elements/extended_touch_target_button.h"
 #import "ios/chrome/browser/shared/ui/elements/new_feature_badge_view.h"
@@ -22,10 +26,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/lens/lens_availability.h"
-#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h"
-#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_delegate.h"
-#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
-#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_container_view.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
@@ -34,6 +34,8 @@
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
+#import "ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.h"
+#import "ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_view.h"
 #import "ios/chrome/common/material_timing.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/elements/gradient_view.h"
@@ -219,6 +221,11 @@
   UIImageView* _accountDiscParticleBadgeImageView;
   // The New Feature badge on the customization menu's entrypoint.
   UIView* _customizationNewFeatureBadge;
+
+  // Constraints to update the `toolbarView`'s postion according to the
+  // `tabGroupIndicatorView`'s visibility.
+  NSLayoutConstraint* _toolbarNoTabGroupIndicartorConstraint;
+  NSLayoutConstraint* _toolbarTabGroupIndicartorConstraint;
 }
 
 #pragma mark - Public
@@ -238,12 +245,15 @@
 - (void)addToolbarView:(UIView*)toolbarView {
   _toolBarView = toolbarView;
   [self addSubview:toolbarView];
+
+  _toolbarNoTabGroupIndicartorConstraint =
+      [toolbarView.topAnchor constraintEqualToAnchor:self.topAnchor];
   [NSLayoutConstraint activateConstraints:@[
     [toolbarView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
     [toolbarView.heightAnchor
         constraintEqualToConstant:content_suggestions::FakeToolbarHeight()],
     [toolbarView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
-    [toolbarView.topAnchor constraintEqualToAnchor:self.topAnchor],
+    _toolbarNoTabGroupIndicartorConstraint,
   ]];
 }
 
@@ -510,6 +520,9 @@
       content_suggestions::SearchFieldWidth(contentWidth, self.traitCollection);
 
   CGFloat percent = [self searchFieldProgressForOffset:offset];
+  if (IsTabGroupIndicatorEnabled()) {
+    [self updateTabGroupIndicatorAvailabilityWithOffset:offset];
+  }
 
   // Update the opacity of the header background color as the user scrolls so
   // that content does not appear beneath it. Since the NTP background might be
@@ -748,6 +761,29 @@
   _customizationNewFeatureBadge.alpha = 0;
 }
 
+- (void)updateTabGroupIndicatorAvailabilityWithOffset:(CGFloat)offset {
+  CHECK(IsTabGroupIndicatorEnabled());
+  offset = fmax(offset, 0);
+
+  BOOL canShowTabStrip = IsRegularXRegularSizeClass(self);
+  BOOL isAvailable = !IsCompactHeight(self) && !canShowTabStrip;
+  _tabGroupIndicatorView.available = isAvailable;
+
+  // Make the view disappear while the indicator is scrolled out of the screen.
+  _tabGroupIndicatorView.alpha =
+      1 - fmax(0, (offset / kTabGroupIndicatorHeight));
+
+  _toolbarTabGroupIndicartorConstraint.constant =
+      kTabGroupIndicatorNTPToolbarMargin - offset;
+  if (_tabGroupIndicatorView.hidden) {
+    _toolbarTabGroupIndicartorConstraint.active = NO;
+    _toolbarNoTabGroupIndicartorConstraint.active = YES;
+  } else {
+    _toolbarNoTabGroupIndicartorConstraint.active = NO;
+    _toolbarTabGroupIndicartorConstraint.active = YES;
+  }
+}
+
 #pragma mark - UITraitEnvironment
 
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
@@ -787,6 +823,34 @@
   return _fakeLocationBar;
 }
 
+#pragma mark - Setters
+
+// Sets tabgroupIndicatorView.
+- (void)setTabGroupIndicatorView:(TabGroupIndicatorView*)view {
+  CHECK(IsTabGroupIndicatorEnabled());
+  _tabGroupIndicatorView = view;
+  _tabGroupIndicatorView.hidden = YES;
+  _tabGroupIndicatorView.translatesAutoresizingMaskIntoConstraints = NO;
+  _tabGroupIndicatorView.showSeparator = YES;
+  [self addSubview:_tabGroupIndicatorView];
+
+  _toolbarTabGroupIndicartorConstraint = [_toolBarView.topAnchor
+      constraintEqualToAnchor:_tabGroupIndicatorView.bottomAnchor
+                     constant:kTabGroupIndicatorNTPToolbarMargin];
+  [NSLayoutConstraint activateConstraints:@[
+    [self.tabGroupIndicatorView.leadingAnchor
+        constraintEqualToAnchor:self.leadingAnchor],
+    [self.tabGroupIndicatorView.trailingAnchor
+        constraintEqualToAnchor:self.trailingAnchor],
+    [self.tabGroupIndicatorView.topAnchor
+        constraintEqualToAnchor:self.topAnchor
+                       constant:kTabGroupIndicatorNTPTopMargin],
+    [_tabGroupIndicatorView.heightAnchor
+        constraintEqualToConstant:kTabGroupIndicatorHeight],
+  ]];
+  [self updateTabGroupIndicatorAvailabilityWithOffset:0];
+}
+
 #pragma mark - Private
 
 // Gets the fonts for the pinned and unpinned fakebox hint label, and sets
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.h b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.h
index 686c332..bebbb3c1 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.h
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.h
@@ -22,6 +22,7 @@
 @protocol LensCommands;
 @class LayoutGuideCenter;
 @class PrimaryToolbarViewController;
+@class TabGroupIndicatorView;
 
 // Controller for the header containing the logo and the fake omnibox, handling
 // the interactions between the header and the collection, and the rest of the
@@ -127,6 +128,9 @@
 // Hides the new feature badge on the Home customization menu's entrypoint.
 - (void)hideBadgeOnCustomizationMenu;
 
+// Sets the tabgroupIndicatorView.
+- (void)setTabGroupIndicatorView:(TabGroupIndicatorView*)view;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_NTP_UI_BUNDLED_NEW_TAB_PAGE_HEADER_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm
index 222573b..97caca35 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm
@@ -42,6 +42,7 @@
 #import "ios/chrome/browser/ui/lens/lens_entrypoint.h"
 #import "ios/chrome/browser/ui/toolbar/public/fakebox_focuser.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
+#import "ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_view.h"
 #import "ios/chrome/common/material_timing.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
@@ -212,6 +213,9 @@
                     safeAreaInsets:(UIEdgeInsets)safeAreaInsets
             animateScrollAnimation:(BOOL)animateScrollAnimation {
   if (self.isShowing) {
+    if (IsTabGroupIndicatorEnabled()) {
+      [self.headerView updateTabGroupIndicatorAvailabilityWithOffset:offset];
+    }
     CGFloat progress =
         self.logoIsShowing || !IsRegularXRegularSizeClass(self)
             ? [self.headerView searchFieldProgressForOffset:offset]
@@ -350,6 +354,10 @@
   [self.headerView hideBadgeOnCustomizationMenu];
 }
 
+- (void)setTabGroupIndicatorView:(TabGroupIndicatorView*)view {
+  self.headerView.tabGroupIndicatorView = view;
+}
+
 #pragma mark - Private
 
 // Initialize and add a search field tap target and a voice search button.
diff --git a/ios/chrome/browser/optimization_guide/model/hints_fetcher_egtest.mm b/ios/chrome/browser/optimization_guide/model/hints_fetcher_egtest.mm
index 646d88f..84baf042 100644
--- a/ios/chrome/browser/optimization_guide/model/hints_fetcher_egtest.mm
+++ b/ios/chrome/browser/optimization_guide/model/hints_fetcher_egtest.mm
@@ -173,8 +173,8 @@
 // optimization guide hints fetching are integration tested. This includes tests
 // that verify hints fetcher failure cases, fetching of hints for multiple open
 // tabs at startup, hints are cleared when browsing history is cleared, etc.
-
-- (void)testHintsFetchBasic {
+// TODO(crbug.com/366045251): Re-enable once fixed.
+- (void)DISABLED_testHintsFetchBasic {
   [ChromeEarlGrey loadURL:GURL("https://foo.com/test")];
   // Wait for the hints to be served.
   GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 42ecdc34..2b1f9628 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -273,6 +273,7 @@
     "//ios/chrome/browser/ui/content_suggestions/cells:constants",
     "//ios/chrome/browser/ui/toolbar/public",
     "//ios/chrome/browser/ui/toolbar/public:constants",
+    "//ios/chrome/browser/ui/toolbar/tab_groups/ui:constants",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/util",
     "//ios/components/ui_util",
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
index 108f598d..e14af9b 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
@@ -75,6 +75,8 @@
   sources = [
     "magic_stack_edit_button_cell.h",
     "magic_stack_edit_button_cell.mm",
+    "magic_stack_module_collection_view_cell.h",
+    "magic_stack_module_collection_view_cell.mm",
     "magic_stack_module_container.h",
     "magic_stack_module_container.mm",
     "magic_stack_module_contents_factory.h",
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
index 4e24c7a..9b6e279c 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
@@ -20,6 +20,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_edit_button_cell.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_layout_configurator.h"
+#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/placeholder_config.h"
 
@@ -240,12 +241,12 @@
   _collectionView.backgroundColor = [UIColor clearColor];
 
   __weak MagicStackCollectionViewController* weakSelf = self;
-  auto configureModuleCell = ^(MagicStackModuleContainer* cell,
+  auto configureModuleCell = ^(MagicStackModuleCollectionViewCell* cell,
                                NSIndexPath* indexPath, MagicStackModule* item) {
     [weakSelf configureCell:cell withItem:item atIndex:indexPath.item];
   };
   _moduleCellRegistration = [UICollectionViewCellRegistration
-      registrationWithCellClass:[MagicStackModuleContainer class]
+      registrationWithCellClass:[MagicStackModuleCollectionViewCell class]
            configurationHandler:configureModuleCell];
 
   auto configureEditButtonCell =
@@ -305,7 +306,7 @@
 }
 
 // Cell configuration handler helper.
-- (void)configureCell:(MagicStackModuleContainer*)cell
+- (void)configureCell:(MagicStackModuleCollectionViewCell*)cell
              withItem:(MagicStackModule*)item
               atIndex:(NSUInteger)index {
   cell.delegate = self.audience;
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.h b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.h
new file mode 100644
index 0000000..f312205
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.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 IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_MAGIC_STACK_MAGIC_STACK_MODULE_COLLECTION_VIEW_CELL_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_MAGIC_STACK_MAGIC_STACK_MODULE_COLLECTION_VIEW_CELL_H_
+
+#import <UIKit/UIKit.h>
+
+enum class ContentSuggestionsModuleType;
+@protocol MagicStackModuleContainerDelegate;
+@class MagicStackModule;
+
+// Cell for a module in the Magic Stack.
+@interface MagicStackModuleCollectionViewCell : UICollectionViewCell
+
+- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
+
+// Configures this container with `config`.
+- (void)configureWithConfig:(MagicStackModule*)config;
+
+// Delegate for this container.
+@property(nonatomic, weak) id<MagicStackModuleContainerDelegate> delegate;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_MAGIC_STACK_MAGIC_STACK_MODULE_COLLECTION_VIEW_CELL_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm
new file mode 100644
index 0000000..41b5a939
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm
@@ -0,0 +1,318 @@
+// 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/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.h"
+
+#import "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
+#import "ios/chrome/browser/push_notification/model/push_notification_settings_util.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
+#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module.h"
+#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.h"
+#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container_delegate.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
+#import "ios/chrome/grit/ios_branded_strings.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util.h"
+#import "ui/base/l10n/l10n_util_mac.h"
+
+namespace {
+
+// The corner radius of this container.
+const float kCornerRadius = 24;
+
+}  // namespace
+
+@interface MagicStackModuleCollectionViewCell () <
+    UIContextMenuInteractionDelegate>
+
+@property(nonatomic, assign) ContentSuggestionsModuleType type;
+
+@end
+
+@implementation MagicStackModuleCollectionViewCell {
+  MagicStackModuleContainer* _moduleContainer;
+  UIContextMenuInteraction* _contextMenuInteraction;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+  self = [super initWithFrame:frame];
+  if (self) {
+    self.contentView.backgroundColor = [UIColor colorNamed:kBackgroundColor];
+    self.layer.cornerRadius = kCornerRadius;
+    self.clipsToBounds = YES;
+
+    _moduleContainer =
+        [[MagicStackModuleContainer alloc] initWithFrame:CGRectZero];
+    _moduleContainer.translatesAutoresizingMaskIntoConstraints = NO;
+    [self addSubview:_moduleContainer];
+    AddSameConstraints(_moduleContainer, self);
+  }
+  return self;
+}
+
+- (void)configureWithConfig:(MagicStackModule*)config {
+  _type = config.type;
+  if ([self allowsLongPress]) {
+    if (!_contextMenuInteraction) {
+      _contextMenuInteraction =
+          [[UIContextMenuInteraction alloc] initWithDelegate:self];
+      [self addInteraction:_contextMenuInteraction];
+    }
+  }
+  [_moduleContainer configureWithConfig:config];
+}
+
+#pragma mark - Setters
+
+- (void)setDelegate:(id<MagicStackModuleContainerDelegate>)delegate {
+  _moduleContainer.delegate = delegate;
+  _delegate = delegate;
+}
+
+#pragma mark UICollectionViewCell Overrides
+
+- (void)prepareForReuse {
+  [super prepareForReuse];
+  if (_contextMenuInteraction) {
+    [self removeInteraction:_contextMenuInteraction];
+    _contextMenuInteraction = nil;
+  }
+  [_moduleContainer resetView];
+}
+
+#pragma mark - UIContextMenuInteractionDelegate
+
+- (UIContextMenuConfiguration*)contextMenuInteraction:
+                                   (UIContextMenuInteraction*)interaction
+                       configurationForMenuAtLocation:(CGPoint)location {
+  CHECK([self allowsLongPress]);
+  __weak MagicStackModuleCollectionViewCell* weakSelf = self;
+  UIContextMenuActionProvider actionProvider =
+      ^(NSArray<UIMenuElement*>* suggestedActions) {
+        return [UIMenu menuWithTitle:[weakSelf contextMenuTitle]
+                            children:[weakSelf contextMenuActions]];
+      };
+  return
+      [UIContextMenuConfiguration configurationWithIdentifier:nil
+                                              previewProvider:nil
+                                               actionProvider:actionProvider];
+}
+
+#pragma mark - Helpers
+
+// Returns the list of actions for the long-press /  context menu.
+- (NSArray<UIAction*>*)contextMenuActions {
+  NSMutableArray<UIAction*>* actions = [[NSMutableArray alloc] init];
+
+  if ((IsSetUpListModuleType(_type) && IsIOSTipsNotificationsEnabled()) ||
+      (_type == ContentSuggestionsModuleType::kSafetyCheck &&
+       IsSafetyCheckNotificationsEnabled())) {
+    [actions addObject:[self toggleNotificationsActionForModuleType:self.type]];
+  }
+
+  [actions addObject:[self hideAction]];
+
+  [actions addObject:[self customizeCardAction]];
+
+  return actions;
+}
+
+// Returns the menu action to hide this module type.
+- (UIAction*)hideAction {
+  __weak __typeof(self) weakSelf = self;
+  UIAction* hideAction = [UIAction
+      actionWithTitle:[self contextMenuHideDescription]
+                image:DefaultSymbolWithPointSize(kHideActionSymbol, 18)
+           identifier:nil
+              handler:^(UIAction* action) {
+                [weakSelf.delegate neverShowModuleType:weakSelf.type];
+              }];
+  hideAction.attributes = UIMenuElementAttributesDestructive;
+  return hideAction;
+}
+
+// Returns the menu action to hide this module type.
+- (UIAction*)customizeCardAction {
+  __weak __typeof(self) weakSelf = self;
+  UIAction* hideAction = [UIAction
+      actionWithTitle:
+          l10n_util::GetNSString(
+              IDS_IOS_MAGIC_STACK_CONTEXT_MENU_CUSTOMIZE_CARDS_TITLE)
+                image:DefaultSymbolWithPointSize(kSliderHorizontalSymbol, 18)
+           identifier:nil
+              handler:^(UIAction* action) {
+                [weakSelf.delegate customizeCardsWasTapped];
+              }];
+  return hideAction;
+}
+
+// `YES` if this container should show a context menu when the user performs a
+// long-press gesture.
+- (BOOL)allowsLongPress {
+  switch (_type) {
+    case ContentSuggestionsModuleType::kTabResumption:
+    case ContentSuggestionsModuleType::kSafetyCheck:
+    case ContentSuggestionsModuleType::kSetUpListSync:
+    case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
+    case ContentSuggestionsModuleType::kSetUpListAutofill:
+    case ContentSuggestionsModuleType::kSetUpListNotifications:
+    case ContentSuggestionsModuleType::kCompactedSetUpList:
+    case ContentSuggestionsModuleType::kParcelTracking:
+      return YES;
+    default:
+      return NO;
+  }
+}
+
+// Returns the menu action to opt-in to Tips Notifications.
+- (UIAction*)toggleNotificationsActionForModuleType:
+    (ContentSuggestionsModuleType)moduleType {
+  const PushNotificationClientId clientId =
+      [self pushNotificationClientId:moduleType];
+
+  BOOL optedIn = [self optedInToNotificationsForClient:clientId];
+
+  __weak __typeof(self) weakSelf = self;
+
+  NSString* title;
+  NSString* symbol;
+
+  int featureTitle = [self pushNotificationTitleMessageId:moduleType];
+
+  if (optedIn) {
+    title = l10n_util::GetNSStringF(
+        IDS_IOS_TIPS_NOTIFICATIONS_CONTEXT_MENU_ITEM_OFF,
+        l10n_util::GetStringUTF16(featureTitle));
+    symbol = kBellSlashSymbol;
+  } else {
+    title =
+        l10n_util::GetNSStringF(IDS_IOS_TIPS_NOTIFICATIONS_CONTEXT_MENU_ITEM,
+                                l10n_util::GetStringUTF16(featureTitle));
+    symbol = kBellSymbol;
+  }
+
+  return [UIAction
+      actionWithTitle:title
+                image:DefaultSymbolWithPointSize(symbol, 18)
+           identifier:nil
+              handler:^(UIAction* action) {
+                if (optedIn) {
+                  [weakSelf.delegate disableNotifications:weakSelf.type];
+                } else {
+                  [weakSelf.delegate enableNotifications:weakSelf.type];
+                }
+              }];
+}
+
+// Returns the `PushNotificationClientId` associated with the specified `type`.
+// Currently, push notifications are exclusively supported by the Set Up List
+// and Safety Check modules.
+- (PushNotificationClientId)pushNotificationClientId:
+    (ContentSuggestionsModuleType)type {
+  // This is only supported for Set Up List and Safety Check modules.
+  CHECK(IsSetUpListModuleType(type) ||
+        type == ContentSuggestionsModuleType::kSafetyCheck);
+
+  if (type == ContentSuggestionsModuleType::kSafetyCheck) {
+    return PushNotificationClientId::kSafetyCheck;
+  }
+
+  if (IsSetUpListModuleType(type)) {
+    return PushNotificationClientId::kTips;
+  }
+
+  NOTREACHED();
+}
+
+// Retrieves the message ID for the push notification feature title associated
+// with the specified `ContentSuggestionsModuleType`. Currently, push
+// notifications are exclusively supported by the Set Up List and Safety Check
+// modules.
+- (int)pushNotificationTitleMessageId:(ContentSuggestionsModuleType)type {
+  // This is only supported for Set Up List and Safety Check modules.
+  CHECK(IsSetUpListModuleType(type) ||
+        type == ContentSuggestionsModuleType::kSafetyCheck);
+
+  if (type == ContentSuggestionsModuleType::kSafetyCheck) {
+    return IDS_IOS_SAFETY_CHECK_TITLE;
+  }
+
+  if (IsSetUpListModuleType(type)) {
+    return content_suggestions::SetUpListTitleStringID();
+  }
+
+  NOTREACHED();
+}
+
+// Returns YES if the user has already opted-in to notifications for the
+// specified `clientId`.
+- (BOOL)optedInToNotificationsForClient:(PushNotificationClientId)clientId {
+  // Currently, push notifications are exclusively supported for the Set Up List
+  // and Safety Check modules.
+  CHECK(clientId == PushNotificationClientId::kTips ||
+        clientId == PushNotificationClientId::kSafetyCheck);
+
+  // IMPORTANT: Notifications for Set Up List and Safety Check are managed
+  // through the app-wide notification settings. If a feature that utilizes
+  // per-profile notification settings is being introduced, ensure a `gaia_id`
+  // is passed to `GetMobileNotificationPermissionStatusForClient()` below.
+  return push_notification_settings::
+      GetMobileNotificationPermissionStatusForClient(clientId, "");
+}
+
+// Title string for the context menu of this container.
+- (NSString*)contextMenuTitle {
+  switch (_type) {
+    case ContentSuggestionsModuleType::kTabResumption:
+      return l10n_util::GetNSString(IDS_IOS_TAB_RESUMPTION_CONTEXT_MENU_TITLE);
+    case ContentSuggestionsModuleType::kSafetyCheck:
+      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_CONTEXT_MENU_TITLE);
+    case ContentSuggestionsModuleType::kSetUpListSync:
+    case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
+    case ContentSuggestionsModuleType::kSetUpListAutofill:
+    case ContentSuggestionsModuleType::kCompactedSetUpList:
+    case ContentSuggestionsModuleType::kSetUpListNotifications:
+      return l10n_util::GetNSString(
+          IDS_IOS_SET_UP_LIST_HIDE_MODULE_CONTEXT_MENU_TITLE);
+    case ContentSuggestionsModuleType::kParcelTracking:
+      return l10n_util::GetNSString(IDS_IOS_PARCEL_TRACKING_CONTEXT_MENU_TITLE);
+    default:
+      NOTREACHED();
+  }
+}
+
+// Descriptor string for hide action of the context menu of this container.
+- (NSString*)contextMenuHideDescription {
+  switch (_type) {
+    case ContentSuggestionsModuleType::kTabResumption:
+      return l10n_util::GetNSString(
+          IDS_IOS_TAB_RESUMPTION_CONTEXT_MENU_DESCRIPTION);
+    case ContentSuggestionsModuleType::kSafetyCheck:
+      return l10n_util::GetNSString(
+          IDS_IOS_SAFETY_CHECK_CONTEXT_MENU_DESCRIPTION);
+    case ContentSuggestionsModuleType::kSetUpListSync:
+    case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
+    case ContentSuggestionsModuleType::kSetUpListAutofill:
+    case ContentSuggestionsModuleType::kSetUpListNotifications:
+    case ContentSuggestionsModuleType::kCompactedSetUpList:
+      return l10n_util::GetNSStringF(
+          IDS_IOS_SET_UP_LIST_HIDE_MODULE_CONTEXT_MENU_DESCRIPTION,
+          l10n_util::GetStringUTF16(
+              content_suggestions::SetUpListTitleStringID()));
+    case ContentSuggestionsModuleType::kParcelTracking:
+      return l10n_util::GetNSStringF(
+          IDS_IOS_PARCEL_TRACKING_CONTEXT_MENU_DESCRIPTION,
+          base::SysNSStringToUTF16(l10n_util::GetNSString(
+              IDS_IOS_CONTENT_SUGGESTIONS_PARCEL_TRACKING_MODULE_TITLE)));
+    default:
+      NOTREACHED();
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.h b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.h
index 91b0d5d..6fd4f6c4 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.h
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.h
@@ -12,22 +12,22 @@
 @class MagicStackModule;
 
 // Container View for a module in the Magic Stack.
-@interface MagicStackModuleContainer : UICollectionViewCell
+@interface MagicStackModuleContainer : UIView
 
 - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
 
 // Configures this container with `config`.
 - (void)configureWithConfig:(MagicStackModule*)config;
 
+// Reset the main configurations of the view.
+- (void)resetView;
+
 // Delegate for this container.
 @property(nonatomic, weak) id<MagicStackModuleContainerDelegate> delegate;
 
 // Returns the title string for the module `type`.
 + (NSString*)titleStringForModule:(ContentSuggestionsModuleType)type;
 
-// The type of this container.
-@property(nonatomic, assign, readonly) ContentSuggestionsModuleType type;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_MAGIC_STACK_MAGIC_STACK_MODULE_CONTAINER_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.mm
index e351409..3a10d12 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.mm
@@ -6,10 +6,7 @@
 
 #import "base/notreached.h"
 #import "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/push_notification/model/push_notification_client_id.h"
-#import "ios/chrome/browser/push_notification/model/push_notification_settings_util.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
-#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/shared/ui/util/rtl_geometry.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
@@ -54,12 +51,7 @@
 
 }  // namespace
 
-@interface MagicStackModuleContainer () <UIContextMenuInteractionDelegate,
-                                         MagicStackModuleContentViewDelegate>
-
-// Redefined as ReadWrite.
-@property(nonatomic, assign, readwrite) ContentSuggestionsModuleType type;
-
+@interface MagicStackModuleContainer () <MagicStackModuleContentViewDelegate>
 @end
 
 @implementation MagicStackModuleContainer {
@@ -76,7 +68,7 @@
   MagicStackModuleContentsFactory* _magicStackModuleContentsFactory;
   NSLayoutConstraint* _containerHeightAnchor;
   NSLayoutConstraint* _contentStackViewBottomMarginAnchor;
-  UIContextMenuInteraction* _contextMenuInteraction;
+  ContentSuggestionsModuleType _type;
 }
 
 - (instancetype)initWithFrame:(CGRect)frame {
@@ -85,11 +77,6 @@
     self.maximumContentSizeCategory = UIContentSizeCategoryAccessibilityMedium;
     _magicStackModuleContentsFactory = [[MagicStackModuleContentsFactory alloc] init];
 
-    self.contentView.backgroundColor = [UIColor colorNamed:kBackgroundColor];
-    self.contentView.layer.cornerRadius = kCornerRadius;
-    self.contentView.clipsToBounds = YES;
-    self.layer.cornerRadius = kCornerRadius;
-
     _titleStackView = [[UIStackView alloc] init];
     _titleStackView.alignment = UIStackViewAlignmentTop;
     _titleStackView.axis = UILayoutConstraintAxisHorizontal;
@@ -185,7 +172,7 @@
         [self.heightAnchor constraintEqualToConstant:kModuleMaxHeight];
     [NSLayoutConstraint activateConstraints:@[ _containerHeightAnchor ]];
 
-    [self.contentView addSubview:_stackView];
+    [self addSubview:_stackView];
     AddSameConstraintsToSidesWithInsets(
         _stackView, self,
         (LayoutSides::kTop | LayoutSides::kLeading | LayoutSides::kTrailing),
@@ -201,7 +188,7 @@
 }
 
 - (void)dealloc {
-  [self resetCell];
+  [self resetView];
 }
 
 // Creates a button with the specified `title` positioned in the module's
@@ -245,13 +232,16 @@
 }
 
 - (void)configureWithConfig:(MagicStackModule*)config {
-  [self resetCell];
+  [self resetView];
   // Ensures that the modules conforms to a height of kModuleMaxHeight. For
   // the MVT when it lives outside of the Magic Stack to stay as close to its
   // intrinsic size as possible, the constraint is configured to be less than
   // or equal to.
   if (config.type == ContentSuggestionsModuleType::kMostVisited &&
       !ShouldPutMostVisitedSitesInMagicStack()) {
+    self.backgroundColor = [UIColor colorNamed:kBackgroundColor];
+    self.layer.cornerRadius = kCornerRadius;
+    self.clipsToBounds = YES;
     _containerHeightAnchor.active = NO;
     _containerHeightAnchor = [self.heightAnchor
         constraintLessThanOrEqualToConstant:kModuleMaxHeight];
@@ -270,13 +260,6 @@
     return;
   }
   _type = config.type;
-  if ([self allowsLongPress]) {
-    if (!_contextMenuInteraction) {
-      _contextMenuInteraction =
-          [[UIContextMenuInteraction alloc] initWithDelegate:self];
-      [self addInteraction:_contextMenuInteraction];
-    }
-  }
 
   _title.text = [MagicStackModuleContainer titleStringForModule:_type];
   _title.accessibilityIdentifier =
@@ -332,6 +315,20 @@
   self.accessibilityElements = accessibilityElements;
 }
 
+- (void)resetView {
+  _title.text = nil;
+  _subtitle.text = nil;
+  _isPlaceholder = NO;
+  if (_placeholderImage) {
+    [_placeholderImage removeFromSuperview];
+    _placeholderImage = nil;
+  }
+  if (_contentView) {
+    [_contentView removeFromSuperview];
+    _contentView = nil;
+  }
+}
+
 // Returns the module's title, if any, given the Magic Stack module `type`.
 + (NSString*)titleStringForModule:(ContentSuggestionsModuleType)type {
   switch (type) {
@@ -415,13 +412,6 @@
   }
 }
 
-#pragma mark UICollectionViewCell Overrides
-
-- (void)prepareForReuse {
-  [super prepareForReuse];
-  [self resetCell];
-}
-
 #pragma mark - UITraitEnvironment
 
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
@@ -439,152 +429,8 @@
   _subtitle.accessibilityIdentifier = subtitle;
 }
 
-#pragma mark - UIContextMenuInteractionDelegate
-
-- (UIContextMenuConfiguration*)contextMenuInteraction:
-                                   (UIContextMenuInteraction*)interaction
-                       configurationForMenuAtLocation:(CGPoint)location {
-  CHECK([self allowsLongPress]);
-  __weak MagicStackModuleContainer* weakSelf = self;
-  UIContextMenuActionProvider actionProvider =
-      ^(NSArray<UIMenuElement*>* suggestedActions) {
-        return [UIMenu menuWithTitle:[weakSelf contextMenuTitle]
-                            children:[weakSelf contextMenuActions]];
-      };
-  return
-      [UIContextMenuConfiguration configurationWithIdentifier:nil
-                                              previewProvider:nil
-                                               actionProvider:actionProvider];
-}
-
 #pragma mark - Helpers
 
-// Returns the list of actions for the long-press /  context menu.
-- (NSArray<UIAction*>*)contextMenuActions {
-  NSMutableArray<UIAction*>* actions = [[NSMutableArray alloc] init];
-
-  if ((IsSetUpListModuleType(self.type) && IsIOSTipsNotificationsEnabled()) ||
-      (self.type == ContentSuggestionsModuleType::kSafetyCheck &&
-       IsSafetyCheckNotificationsEnabled())) {
-    [actions addObject:[self toggleNotificationsActionForModuleType:self.type]];
-  }
-
-  [actions addObject:[self hideAction]];
-
-  [actions addObject:[self customizeCardAction]];
-
-  return actions;
-}
-
-// Returns the menu action to hide this module type.
-- (UIAction*)hideAction {
-  __weak __typeof(self) weakSelf = self;
-  UIAction* hideAction = [UIAction
-      actionWithTitle:[self contextMenuHideDescription]
-                image:DefaultSymbolWithPointSize(kHideActionSymbol, 18)
-           identifier:nil
-              handler:^(UIAction* action) {
-                [weakSelf.delegate neverShowModuleType:weakSelf.type];
-              }];
-  hideAction.attributes = UIMenuElementAttributesDestructive;
-  return hideAction;
-}
-
-// Returns the menu action to hide this module type.
-- (UIAction*)customizeCardAction {
-  __weak __typeof(self) weakSelf = self;
-  UIAction* hideAction = [UIAction
-      actionWithTitle:
-          l10n_util::GetNSString(
-              IDS_IOS_MAGIC_STACK_CONTEXT_MENU_CUSTOMIZE_CARDS_TITLE)
-                image:DefaultSymbolWithPointSize(kSliderHorizontalSymbol, 18)
-           identifier:nil
-              handler:^(UIAction* action) {
-                [weakSelf.delegate customizeCardsWasTapped];
-              }];
-  return hideAction;
-}
-
-// Returns the `PushNotificationClientId` associated with the specified `type`.
-// Currently, push notifications are exclusively supported by the Set Up List
-// and Safety Check modules.
-- (PushNotificationClientId)pushNotificationClientId:
-    (ContentSuggestionsModuleType)type {
-  // This is only supported for Set Up List and Safety Check modules.
-  CHECK(IsSetUpListModuleType(type) ||
-        type == ContentSuggestionsModuleType::kSafetyCheck);
-
-  if (type == ContentSuggestionsModuleType::kSafetyCheck) {
-    return PushNotificationClientId::kSafetyCheck;
-  }
-
-  if (IsSetUpListModuleType(type)) {
-    return PushNotificationClientId::kTips;
-  }
-
-  NOTREACHED();
-}
-
-// Retrieves the message ID for the push notification feature title associated
-// with the specified `ContentSuggestionsModuleType`. Currently, push
-// notifications are exclusively supported by the Set Up List and Safety Check
-// modules.
-- (int)pushNotificationTitleMessageId:(ContentSuggestionsModuleType)type {
-  // This is only supported for Set Up List and Safety Check modules.
-  CHECK(IsSetUpListModuleType(type) ||
-        type == ContentSuggestionsModuleType::kSafetyCheck);
-
-  if (type == ContentSuggestionsModuleType::kSafetyCheck) {
-    return IDS_IOS_SAFETY_CHECK_TITLE;
-  }
-
-  if (IsSetUpListModuleType(type)) {
-    return content_suggestions::SetUpListTitleStringID();
-  }
-
-  NOTREACHED();
-}
-
-// Returns the menu action to opt-in to Tips Notifications.
-- (UIAction*)toggleNotificationsActionForModuleType:
-    (ContentSuggestionsModuleType)moduleType {
-  const PushNotificationClientId clientId =
-      [self pushNotificationClientId:moduleType];
-
-  BOOL optedIn = [self optedInToNotificationsForClient:clientId];
-
-  __weak __typeof(self) weakSelf = self;
-
-  NSString* title;
-  NSString* symbol;
-
-  int featureTitle = [self pushNotificationTitleMessageId:moduleType];
-
-  if (optedIn) {
-    title = l10n_util::GetNSStringF(
-        IDS_IOS_TIPS_NOTIFICATIONS_CONTEXT_MENU_ITEM_OFF,
-        l10n_util::GetStringUTF16(featureTitle));
-    symbol = kBellSlashSymbol;
-  } else {
-    title =
-        l10n_util::GetNSStringF(IDS_IOS_TIPS_NOTIFICATIONS_CONTEXT_MENU_ITEM,
-                                l10n_util::GetStringUTF16(featureTitle));
-    symbol = kBellSymbol;
-  }
-
-  return [UIAction
-      actionWithTitle:title
-                image:DefaultSymbolWithPointSize(symbol, 18)
-           identifier:nil
-              handler:^(UIAction* action) {
-                if (optedIn) {
-                  [weakSelf.delegate disableNotifications:weakSelf.type];
-                } else {
-                  [weakSelf.delegate enableNotifications:weakSelf.type];
-                }
-              }];
-}
-
 // Handles taps on the "See More" button.
 - (void)seeMoreButtonWasTapped:(UIButton*)button {
   [_delegate seeMoreWasTappedForModuleType:_type];
@@ -595,24 +441,6 @@
   [_delegate enableNotifications:_type];
 }
 
-// `YES` if this container should show a context menu when the user performs a
-// long-press gesture.
-- (BOOL)allowsLongPress {
-  switch (_type) {
-    case ContentSuggestionsModuleType::kTabResumption:
-    case ContentSuggestionsModuleType::kSafetyCheck:
-    case ContentSuggestionsModuleType::kSetUpListSync:
-    case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
-    case ContentSuggestionsModuleType::kSetUpListAutofill:
-    case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kCompactedSetUpList:
-    case ContentSuggestionsModuleType::kParcelTracking:
-      return YES;
-    default:
-      return NO;
-  }
-}
-
 // Determines if a subtitle should be displayed based on the
 // `ContentSuggestionsModuleType`. Returns `NO` if a Magic Stack module action
 // button is currently displayed.
@@ -658,88 +486,4 @@
   }
 }
 
-// Title string for the context menu of this container.
-- (NSString*)contextMenuTitle {
-  switch (_type) {
-    case ContentSuggestionsModuleType::kTabResumption:
-      return l10n_util::GetNSString(IDS_IOS_TAB_RESUMPTION_CONTEXT_MENU_TITLE);
-    case ContentSuggestionsModuleType::kSafetyCheck:
-      return l10n_util::GetNSString(IDS_IOS_SAFETY_CHECK_CONTEXT_MENU_TITLE);
-    case ContentSuggestionsModuleType::kSetUpListSync:
-    case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
-    case ContentSuggestionsModuleType::kSetUpListAutofill:
-    case ContentSuggestionsModuleType::kCompactedSetUpList:
-    case ContentSuggestionsModuleType::kSetUpListNotifications:
-      return l10n_util::GetNSString(
-          IDS_IOS_SET_UP_LIST_HIDE_MODULE_CONTEXT_MENU_TITLE);
-    case ContentSuggestionsModuleType::kParcelTracking:
-      return l10n_util::GetNSString(IDS_IOS_PARCEL_TRACKING_CONTEXT_MENU_TITLE);
-    default:
-      NOTREACHED();
-  }
-}
-
-// Descriptor string for hide action of the context menu of this container.
-- (NSString*)contextMenuHideDescription {
-  switch (_type) {
-    case ContentSuggestionsModuleType::kTabResumption:
-      return l10n_util::GetNSString(
-          IDS_IOS_TAB_RESUMPTION_CONTEXT_MENU_DESCRIPTION);
-    case ContentSuggestionsModuleType::kSafetyCheck:
-      return l10n_util::GetNSString(
-          IDS_IOS_SAFETY_CHECK_CONTEXT_MENU_DESCRIPTION);
-    case ContentSuggestionsModuleType::kSetUpListSync:
-    case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
-    case ContentSuggestionsModuleType::kSetUpListAutofill:
-    case ContentSuggestionsModuleType::kSetUpListNotifications:
-    case ContentSuggestionsModuleType::kCompactedSetUpList:
-      return l10n_util::GetNSStringF(
-          IDS_IOS_SET_UP_LIST_HIDE_MODULE_CONTEXT_MENU_DESCRIPTION,
-          l10n_util::GetStringUTF16(
-              content_suggestions::SetUpListTitleStringID()));
-    case ContentSuggestionsModuleType::kParcelTracking:
-      return l10n_util::GetNSStringF(
-          IDS_IOS_PARCEL_TRACKING_CONTEXT_MENU_DESCRIPTION,
-          base::SysNSStringToUTF16(l10n_util::GetNSString(
-              IDS_IOS_CONTENT_SUGGESTIONS_PARCEL_TRACKING_MODULE_TITLE)));
-    default:
-      NOTREACHED();
-  }
-}
-
-// Reset the main configurations of the cell.
-- (void)resetCell {
-  _title.text = nil;
-  _subtitle.text = nil;
-  _isPlaceholder = NO;
-  if (_placeholderImage) {
-    [_placeholderImage removeFromSuperview];
-    _placeholderImage = nil;
-  }
-  if (_contentView) {
-    [_contentView removeFromSuperview];
-    _contentView = nil;
-  }
-  if (_contextMenuInteraction) {
-    [self removeInteraction:_contextMenuInteraction];
-    _contextMenuInteraction = nil;
-  }
-}
-
-// Returns YES if the user has already opted-in to notifications for the
-// specified `clientId`.
-- (BOOL)optedInToNotificationsForClient:(PushNotificationClientId)clientId {
-  // Currently, push notifications are exclusively supported for the Set Up List
-  // and Safety Check modules.
-  CHECK(clientId == PushNotificationClientId::kTips ||
-        clientId == PushNotificationClientId::kSafetyCheck);
-
-  // IMPORTANT: Notifications for Set Up List and Safety Check are managed
-  // through the app-wide notification settings. If a feature that utilizes
-  // per-profile notification settings is being introduced, ensure a `gaia_id`
-  // is passed to `GetMobileNotificationPermissionStatusForClient()` below.
-  return push_notification_settings::
-      GetMobileNotificationPermissionStatusForClient(clientId, "");
-}
-
 @end
diff --git a/ios/chrome/browser/ui/page_info/page_info_egtest.mm b/ios/chrome/browser/ui/page_info/page_info_egtest.mm
index c124b9b..02be5b9 100644
--- a/ios/chrome/browser/ui/page_info/page_info_egtest.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_egtest.mm
@@ -511,7 +511,15 @@
 // Tests that the Last Visited section is displayed when there exists a previous
 // visit, and also, it tests that the correct timestamp of the last visit is
 // presented.
-- (void)testLastVisitedSectionDisplaysYesterday {
+// TODO(crbug.com/366003628): Test is flaky on device.
+#if TARGET_OS_SIMULATOR
+#define MAYBE_testLastVisitedSectionDisplaysYesterday \
+  testLastVisitedSectionDisplaysYesterday
+#else
+#define MAYBE_testLastVisitedSectionDisplaysYesterday \
+  DISABLED_testLastVisitedSectionDisplaysYesterday
+#endif
+- (void)MAYBE_testLastVisitedSectionDisplaysYesterday {
   GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
   GURL URL("https://www.example.com/");
 
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm
index 8a24326..bcb38a7 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/browser/ui/settings/password/password_details/password_details_mediator.h"
 
+#import <algorithm>
 #import <memory>
 #import <utility>
 #import <vector>
@@ -13,6 +14,7 @@
 #import "base/memory/raw_ptr.h"
 #import "base/ranges/algorithm.h"
 #import "base/strings/sys_string_conversions.h"
+#import "base/time/time.h"
 #import "build/branding_buildflags.h"
 #import "components/password_manager/core/browser/features/password_manager_features_util.h"
 #import "components/password_manager/core/browser/password_form.h"
@@ -46,6 +48,19 @@
 
 namespace {
 
+base::Time GetLastUsedModifiedOrCreatedTime(
+    password_manager::SavedPasswordsPresenter* saved_passwords_presenter,
+    const CredentialUIEntry& entry) {
+  base::Time time = entry.last_used_time;
+  for (const password_manager::PasswordForm& form :
+       saved_passwords_presenter->GetCorrespondingPasswordForms(entry)) {
+    time = std::max(time, form.date_last_used);
+    time = std::max(time, form.date_password_modified);
+    time = std::max(time, form.date_created);
+  }
+  return time;
+}
+
 bool MatchesRealmUsernamePasswordAndCreationTime(
     CredentialDetails* credentialDetails,
     const CredentialUIEntry& credential) {
@@ -227,8 +242,9 @@
 }
 
 - (void)setConsumer:(id<PasswordDetailsConsumer>)consumer {
-  if (_consumer == consumer)
+  if (_consumer == consumer) {
     return;
+  }
   _consumer = consumer;
 
   // The email might be empty and the callee handles that.
@@ -321,7 +337,10 @@
       [self conflictingAccountPassword:credentialDetails];
   DCHECK(localCredential != _credentials.end());
   DCHECK(accountCredential.has_value());
-  if (localCredential->last_used_time < accountCredential->last_used_time) {
+  if (GetLastUsedModifiedOrCreatedTime(self.savedPasswordsPresenter,
+                                       *localCredential) <
+      GetLastUsedModifiedOrCreatedTime(self.savedPasswordsPresenter,
+                                       *accountCredential)) {
     [self removeCredential:credentialDetails];
     return;
   }
diff --git a/ios/chrome/browser/ui/toolbar/tab_groups/test/tab_group_indicator_egtest.mm b/ios/chrome/browser/ui/toolbar/tab_groups/test/tab_group_indicator_egtest.mm
index e1fce19..5a01987a 100644
--- a/ios/chrome/browser/ui/toolbar/tab_groups/test/tab_group_indicator_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/tab_groups/test/tab_group_indicator_egtest.mm
@@ -31,7 +31,9 @@
 
 // Matcher for the tab group indicator view.
 id<GREYMatcher> TabGroupIndicatorViewMatcher() {
-  return grey_accessibilityID(kTabGroupIndicatorViewIdentifier);
+  return grey_allOf(
+      grey_ancestor(grey_accessibilityID(kTabGroupIndicatorViewIdentifier)),
+      grey_sufficientlyVisible(), nil);
 }
 
 // Returns a matcher for the tab group indicator view with `title` as title.
@@ -131,7 +133,7 @@
 
   // Check that the indicator is not visible.
   [[EarlGrey selectElementWithMatcher:TabGroupIndicatorViewMatcher()]
-      assertWithMatcher:grey_notVisible()];
+      assertWithMatcher:grey_nil()];
 
   // Create a tab cell.
   [ChromeEarlGrey loadURL:GetQueryTitleURL(self.testServer, kTab1Title)];
@@ -173,7 +175,7 @@
 
   // Check that the indicator is not visible.
   [[EarlGrey selectElementWithMatcher:TabGroupIndicatorViewMatcher()]
-      assertWithMatcher:grey_notVisible()];
+      assertWithMatcher:grey_nil()];
 
   // Create a tab cell.
   [ChromeEarlGrey loadURL:GetQueryTitleURL(self.testServer, kTab1Title)];
@@ -186,7 +188,7 @@
 
   // Check that the indicator is still not visible.
   [[EarlGrey selectElementWithMatcher:TabGroupIndicatorViewMatcher()]
-      assertWithMatcher:grey_notVisible()];
+      assertWithMatcher:grey_nil()];
 }
 
 // Tests that opening a new tab in the group from the tab group indicator menu
@@ -386,7 +388,56 @@
 
   // Check that the indicator is not visible.
   [[EarlGrey selectElementWithMatcher:TabGroupIndicatorViewMatcher()]
-      assertWithMatcher:grey_notVisible()];
+      assertWithMatcher:grey_nil()];
+}
+
+// Tests that the tab group indicator is correctly displayed on the NTP.
+- (void)testTabGroupIndicatorNTP {
+  if ([ChromeEarlGrey isIPadIdiom]) {
+    EARL_GREY_TEST_SKIPPED(@"On iPad, the tab indicator is not displauyed if "
+                           @"the tab strip is visible.");
+  }
+
+  // Check that the indicator is not visible.
+  [[EarlGrey selectElementWithMatcher:TabGroupIndicatorViewMatcher()]
+      assertWithMatcher:grey_nil()];
+
+  CreateDefaultTabGroupAndOpenMenu(self.testServer);
+
+  // Tap on the "New tab in group" button.
+  [[EarlGrey
+      selectElementWithMatcher:MenuButtonMatcher(
+                                   IDS_IOS_CONTENT_CONTEXT_NEWTABINGROUP)]
+      performAction:grey_tap()];
+
+  // Check that there are now two tabs and the current tab has changed.
+  GREYAssertEqual(2, [ChromeEarlGrey mainTabCount],
+                  @"Expected 2 tabs to be present.");
+  NSString* newTabTitle = [ChromeEarlGrey currentTabTitle];
+  GREYAssertNotEqual(kTab1Title, newTabTitle,
+                     @"New current tab should have a different title");
+
+  // Check that the indicator is visible.
+  [[EarlGrey
+      selectElementWithMatcher:TabGroupIndicatorViewMatcherWithGroupTitle(
+                                   l10n_util::GetPluralNSStringF(
+                                       IDS_IOS_TAB_GROUP_TABS_NUMBER, 2))]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
+  // Scroll down the NTP and check that the indicator is not visible.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
+      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
+  [[EarlGrey selectElementWithMatcher:TabGroupIndicatorViewMatcher()]
+      assertWithMatcher:grey_nil()];
+
+  // Scroll up the NTP and check that the indicator is visible.
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::NTPCollectionView()]
+      performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];
+  [[EarlGrey
+      selectElementWithMatcher:TabGroupIndicatorViewMatcherWithGroupTitle(
+                                   l10n_util::GetPluralNSStringF(
+                                       IDS_IOS_TAB_GROUP_TABS_NUMBER, 2))]
+      assertWithMatcher:grey_sufficientlyVisible()];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.h b/ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.h
index 62c4fe5b..c7380a7 100644
--- a/ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.h
+++ b/ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.h
@@ -16,6 +16,11 @@
 // Margin between the tab group indicator view colored dot and label.
 extern const CGFloat kTabGroupIndicatorSeparationMargin;
 
+// Top margin for the NTP tab group indicator view.
+extern const CGFloat kTabGroupIndicatorNTPTopMargin;
+// Margin between the NTP tab group indicator and the toolbar.
+extern const CGFloat kTabGroupIndicatorNTPToolbarMargin;
+
 // Accessibility identifier for the tab group indicator view.
 extern NSString* const kTabGroupIndicatorViewIdentifier;
 
diff --git a/ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.mm b/ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.mm
index 03c7ed69..cbcf8b8 100644
--- a/ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.mm
+++ b/ios/chrome/browser/ui/toolbar/tab_groups/ui/tab_group_indicator_constants.mm
@@ -9,5 +9,8 @@
 const CGFloat kTabGroupIndicatorColoredDotSize = 12.0f;
 const CGFloat kTabGroupIndicatorSeparationMargin = 5.0f;
 
+const CGFloat kTabGroupIndicatorNTPTopMargin = -6.0f;
+const CGFloat kTabGroupIndicatorNTPToolbarMargin = 8.0f;
+
 NSString* const kTabGroupIndicatorViewIdentifier =
     @"kTabGroupIndicatorViewIdentifier";
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
index 836cd26..6191c8a0 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -362,7 +362,8 @@
 }
 
 // Tests typing in the omnibox.
-- (void)testToolbarOmniboxTyping {
+// TODO(crbug.com/365987488): Enable when bug is fixed.
+- (void)DISABLED_testToolbarOmniboxTyping {
   [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
       performAction:grey_tap()];
 
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 6a542556..1c6a192 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-ce12c236cf1ce594bcace4b77d5d42114451333a
\ No newline at end of file
+b9472a72c0b73f8e08b5ec472e098c9ed10139f9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 78fa8a8a..034216f 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-5802ed78aed51a0597899ec4c989357085dbda29
\ No newline at end of file
+390d546785ce9d7a59456ff4e357b8e9717e09ca
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index db6bb19..b98f9c6a 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-64ac151e1a185c29d4ba6578f81ae405f3530a04
\ No newline at end of file
+fda9d34479d342ef5b4c23ab1fcc47d4f00fd16e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index cadec43a1..ea656e2 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-96a213ad9dc0c76c871850e231680e099c30dd9b
\ No newline at end of file
+c5460b7ec98de957222778d2f9455f956b4645d5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index d787da4..9eac8611 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a93af04596b768d2f0031d6a1124695a3a64f946
\ No newline at end of file
+89b374fc24302c1db7013d1c043e808a5a4d0210
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index e0680b0..02aab73 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-326a727940495f9a17ee4ff24eddedfa7b96a6e0
\ No newline at end of file
+217e17ecf8a19417e3e440b5ffc70eff80e1e999
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 6e7fbc2d..93e0534b 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-2caa0ea2d061998660b791eef4ce22e95cf21df3
\ No newline at end of file
+578e4fde93a2c96f9fb1f1aeaf5a4404ce0cdf81
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 2baa5445..459b2e4 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-9195c87d4a7aa644261ad22b2e5a917ae8c4f9ef
\ No newline at end of file
+8c8ee2ff56369f186158f35ff4052c7865ed50be
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index fb6a6e6..1ae1c86 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-0d531fef12fbdf484af7c82e490f09073e2adc96
\ No newline at end of file
+2bf1cad72d492e505095254b0368dd55f6a71955
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index 572b900..67c9e627 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-eb20a9abf4a28396c5ba74b82b6076285667de79
\ No newline at end of file
+7b3d20ea19f07e60fb976d41ffd0b3920269a859
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 7e37da8..b2fa34721 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f13e5a0fbddd3107d9cfd8253031ebc6d61399ef
\ No newline at end of file
+c5740e50c335ac7adb62955165b3dc559a878cb5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index f4bf05ec..fe8d2f6d 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-a9a0fb0cecc016049a4dfe71bbc04abba9add813
\ No newline at end of file
+92fd6ac152f59186ea3ee6237c3d382c4954cc8a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index e943bb1..43d7eba 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-0847e397bbca000e6a274fbf039308c80726b93e
\ No newline at end of file
+443f88c32bbea970216dff0ec3736ea5ddb88298
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 1ac68810..d62f95a8 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-921909a92f987a728529978614392eb23c66ef09
\ No newline at end of file
+44443a871be4a14888bdd34c6b7e8aa75b761da5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index c40bf94..28eb656 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-13a6f443b88b54953b665fbc3aad13b4c496c135
\ No newline at end of file
+b0b4685ecb23ea46e68f350139d175771597760f
\ No newline at end of file
diff --git a/ios/web/find_in_page/BUILD.gn b/ios/web/find_in_page/BUILD.gn
index e199422..cc2f3dd 100644
--- a/ios/web/find_in_page/BUILD.gn
+++ b/ios/web/find_in_page/BUILD.gn
@@ -53,6 +53,7 @@
 
   deps = [
     ":compile_ts",
+    "//ios/web/public/js_messaging:error_reporting",
     "//ios/web/public/js_messaging:gcrweb",
   ]
 }
diff --git a/ios/web/find_in_page/resources/find_in_page_native_api.ts b/ios/web/find_in_page/resources/find_in_page_native_api.ts
index 1eae01a0..9d2d7cf 100644
--- a/ios/web/find_in_page/resources/find_in_page_native_api.ts
+++ b/ios/web/find_in_page/resources/find_in_page_native_api.ts
@@ -11,12 +11,11 @@
   MAX_VISIBLE_ELEMENTS,
   TIMEOUT
 } from '//ios/web/find_in_page/resources/find_in_page_constants.js';
-
 import {Match, PartialMatch, Replacement, Section, Timer} from
     '//ios/web/find_in_page/resources/find_in_page.js';
 import {createRegex, escapeHTML} from
     '//ios/web/find_in_page/resources/find_in_page_utils.js';
-
+import {catchAndReportErrors} from '//ios/web/public/js_messaging/resources/error_reporting.js';
 import {gCrWeb} from '//ios/web/public/js_messaging/resources/gcrweb.js';
 // clang-format on
 
@@ -341,36 +340,38 @@
  * @param {number} timeout Maximum time to run.
  * @return {number} that represents the total matches found.
  */
-function findString(string: string, timeout: number): number {
-  // Enable findInPage module if hasn't been done yet.
-  if (!gCrWeb.findInPage.hasInitialized) {
-    enable_();
-    gCrWeb.findInPage.hasInitialized = true;
-  }
+function findString(string: string, timeout: number): number | unknown {
+  return catchAndReportErrors(function() {
+    // Enable findInPage module if hasn't been done yet.
+    if (!gCrWeb.findInPage.hasInitialized) {
+      enable_();
+      gCrWeb.findInPage.hasInitialized = true;
+    }
 
-  if (!searchStateIsClean_) {
-    // Clean up a previous run.
-    cleanUp_();
-  }
-  if (!string) {
-    // No searching for emptyness.
-    return 0;
-  }
+    if (!searchStateIsClean_) {
+      // Clean up a previous run.
+      cleanUp_();
+    }
+    if (!string) {
+      // No searching for emptyness.
+      return 0;
+    }
 
-  // Holds what nodes we have not processed yet.
-  gCrWeb.findInPage.stack = [document.body];
+    // Holds what nodes we have not processed yet.
+    gCrWeb.findInPage.stack = [document.body];
 
-  // Number of visible matches found.
-  visibleMatchCount_ = 0;
+    // Number of visible matches found.
+    visibleMatchCount_ = 0;
 
-  // Index tracking variables so search can be broken up into multiple calls.
-  visibleMatchesCountIndexIterator_ = 0;
+    // Index tracking variables so search can be broken up into multiple calls.
+    visibleMatchesCountIndexIterator_ = 0;
 
-  gCrWeb.findInPage.regex = createRegex(string);
+    gCrWeb.findInPage.regex = createRegex(string);
 
-  searchInProgress_ = true;
+    searchInProgress_ = true;
 
-  return pumpSearch(timeout);
+    return pumpSearch(timeout);
+  });
 };
 
 /**
@@ -391,111 +392,113 @@
  * @param {number} timeout Only run find in page until timeout.
  * @return {number} that represents the total matches found.
  */
-function pumpSearch(timeout: number): number {
-  // TODO(crbug.com/41420794): It would be better if this DCHECKed.
-  if (searchInProgress_ == false) {
-    return 0;
-  }
-
-  searchStateIsClean_ = false;
-
-  let timer = new Timer(timeout);
-
-  // Go through every node in DFS fashion.
-  while (gCrWeb.findInPage.stack.length) {
-    let node = gCrWeb.findInPage.stack.pop();
-    let children = node.childNodes;
-    if (children && children.length) {
-      // add all (reasonable) children
-      for (let i = children.length - 1; i >= 0; --i) {
-        let child = children[i];
-        if ((child.nodeType == 1 || child.nodeType == 3) &&
-            !IGNORE_NODE_NAMES.has(child.nodeName)) {
-          gCrWeb.findInPage.stack.push(children[i]);
-        }
-      }
+function pumpSearch(timeout: number): number | unknown {
+  return catchAndReportErrors(function() {
+    // TODO(crbug.com/41420794): It would be better if this DCHECKed.
+    if (searchInProgress_ == false) {
+      return 0;
     }
 
-    // Build up |allText_| and |sections_|.
-    if (node.nodeType == 3 && node.parentNode) {
-      sections_.push(new Section(
-          allText_.length, allText_.length + node.textContent.length, node));
-      allText_ += node.textContent.toLowerCase();
-    }
+    searchStateIsClean_ = false;
 
-    if (timer.overtime()) {
-      return TIMEOUT;
-    }
-  }
+    let timer = new Timer(timeout);
 
-  // Do regex match in |allText_|, create |matches| and |replacements|. The
-  // regex is set on __gCrWeb, so its state is kept between continuous calls on
-  // pumpSearch.
-  let regex = gCrWeb.findInPage.regex;
-  if (regex) {
-    for (let res; res = regex.exec(allText_);) {
-      // The range of current Match in |allText_| is [begin, end).
-      let begin = res.index;
-      let end = begin + res[0].length;
-      gCrWeb.findInPage.matches.push(new Match());
-
-      // Find the Section where current Match starts.
-      let oldSectionIndex = sectionsIndex_;
-      let newSectionIndex = findFirstSectionEndsAfter_(begin);
-      // If current Match starts at a new Section, process current Section and
-      // move to the new Section.
-      if (newSectionIndex > oldSectionIndex) {
-        processPartialMatchesInCurrentSection();
-        sectionsIndex_ = newSectionIndex;
-      }
-
-      // Create all PartialMatches of current Match.
-      while (true) {
-        let section = sections_[sectionsIndex_];
-        if (!section) {
-          break;
-        }
-        partialMatches_.push(new PartialMatch(
-            matchId_, Math.max(section.begin, begin),
-            Math.min(section.end, end)));
-        // If current Match.end exceeds current Section.end, process current
-        // Section and move to next Section.
-        if (section.end < end) {
-          processPartialMatchesInCurrentSection();
-          ++sectionsIndex_;
-        } else {
-          // Current Match ends in current Section.
-          break;
+    // Go through every node in DFS fashion.
+    while (gCrWeb.findInPage.stack.length) {
+      let node = gCrWeb.findInPage.stack.pop();
+      let children = node.childNodes;
+      if (children && children.length) {
+        // add all (reasonable) children
+        for (let i = children.length - 1; i >= 0; --i) {
+          let child = children[i];
+          if ((child.nodeType == 1 || child.nodeType == 3) &&
+              !IGNORE_NODE_NAMES.has(child.nodeName)) {
+            gCrWeb.findInPage.stack.push(children[i]);
+          }
         }
       }
-      ++matchId_;
+
+      // Build up `allText_` and `sections_`.
+      if (node.nodeType == 3 && node.parentNode) {
+        sections_.push(new Section(
+            allText_.length, allText_.length + node.textContent.length, node));
+        allText_ += node.textContent.toLowerCase();
+      }
 
       if (timer.overtime()) {
         return TIMEOUT;
       }
     }
-    // Process remaining PartialMatches.
-    processPartialMatchesInCurrentSection();
-    gCrWeb.findInPage.regex = undefined;
-  }
 
-  // Execute replacements to highlight search results.
-  for (let i = replacementsIndex_; i < replacements_.length; ++i) {
-    if (timer.overtime()) {
-      replacementsIndex_ = i;
-      return TIMEOUT;
+    // Do regex match in `allText_`, create `matches` and `replacements`. The
+    // regex is set on __gCrWeb, so its state is kept between continuous calls
+    // on pumpSearch.
+    let regex = gCrWeb.findInPage.regex;
+    if (regex) {
+      for (let res; res = regex.exec(allText_);) {
+        // The range of current Match in `allText_` is [begin, end).
+        let begin = res.index;
+        let end = begin + res[0].length;
+        gCrWeb.findInPage.matches.push(new Match());
+
+        // Find the Section where current Match starts.
+        let oldSectionIndex = sectionsIndex_;
+        let newSectionIndex = findFirstSectionEndsAfter_(begin);
+        // If current Match starts at a new Section, process current Section and
+        // move to the new Section.
+        if (newSectionIndex > oldSectionIndex) {
+          processPartialMatchesInCurrentSection();
+          sectionsIndex_ = newSectionIndex;
+        }
+
+        // Create all PartialMatches of current Match.
+        while (true) {
+          let section = sections_[sectionsIndex_];
+          if (!section) {
+            break;
+          }
+          partialMatches_.push(new PartialMatch(
+              matchId_, Math.max(section.begin, begin),
+              Math.min(section.end, end)));
+          // If current Match.end exceeds current Section.end, process current
+          // Section and move to next Section.
+          if (section.end < end) {
+            processPartialMatchesInCurrentSection();
+            ++sectionsIndex_;
+          } else {
+            // Current Match ends in current Section.
+            break;
+          }
+        }
+        ++matchId_;
+
+        if (timer.overtime()) {
+          return TIMEOUT;
+        }
+      }
+      // Process remaining PartialMatches.
+      processPartialMatchesInCurrentSection();
+      gCrWeb.findInPage.regex = undefined;
     }
-    const replacement = replacements_[i];
-    if (replacement) {
-      replacement.doSwap();
+
+    // Execute replacements to highlight search results.
+    for (let i = replacementsIndex_; i < replacements_.length; ++i) {
+      if (timer.overtime()) {
+        replacementsIndex_ = i;
+        return TIMEOUT;
+      }
+      const replacement = replacements_[i];
+      if (replacement) {
+        replacement.doSwap();
+      }
     }
-  }
 
-  let visibleMatchCount = countVisibleMatches_(timer);
+    let visibleMatchCount = countVisibleMatches_(timer);
 
-  searchInProgress_ = false;
+    searchInProgress_ = false;
 
-  return visibleMatchCount;
+    return visibleMatchCount;
+  });
 };
 
 /**
@@ -512,90 +515,92 @@
  * match index.
  */
 function selectAndScrollToVisibleMatch(index: number):
-    {matches: number, index: number, contextString?: string} {
-  if (index >= visibleMatchCount_ || index < 0) {
-    // Do nothing if invalid index is passed or if there are no matches.
-    return {matches: visibleMatchCount_, index: selectedMatchIndex_};
-  }
-
-  // Remove previous highlight.
-  let match = getCurrentSelectedMatch_();
-  if (match) {
-    match.removeSelectHighlight();
-  }
-
-  // Recalculate total visible matches in case it has changed.
-  let visibleMatchCount = countVisibleMatches_(null);
-
-  if (visibleMatchCount == 0) {
-    selectedMatchIndex_ = -1;
-    selectedVisibleMatchIndex_ = -1;
-    return {matches: visibleMatchCount, index: -1};
-  }
-
-  if (index >= visibleMatchCount) {
-    // There are no longer that many visible matches.
-    // Select the last match if moving to previous match.
-    // Select the first currently visible match if moving to next match.
-    index = index > selectedVisibleMatchIndex_ ? 0 : visibleMatchCount - 1;
-  }
-
-  let total_match_index = 0;
-  var visible_match_count = index;
-  // Select the |index|-th visible match.
-  while (total_match_index < gCrWeb.findInPage.matches.length) {
-    if (gCrWeb.findInPage.matches[total_match_index].visible()) {
-      visible_match_count--;
-      if (visible_match_count < 0) {
-        break;
-      }
+    {matches: number, index: number, contextString?: string} | unknown {
+  return catchAndReportErrors(function() {
+    if (index >= visibleMatchCount_ || index < 0) {
+      // Do nothing if invalid index is passed or if there are no matches.
+      return {matches: visibleMatchCount_, index: selectedMatchIndex_};
     }
-    total_match_index++;
-  }
 
-  selectedMatchIndex_ = total_match_index;
-  selectedVisibleMatchIndex_ = index;
+    // Remove previous highlight.
+    let match = getCurrentSelectedMatch_();
+    if (match) {
+      match.removeSelectHighlight();
+    }
 
-  match = getCurrentSelectedMatch_();
-  if (!match) {
-    return {matches: visibleMatchCount, index: -1};
-  }
-  match.addSelectHighlight();
-  scrollToCurrentlySelectedMatch_();
+    // Recalculate total visible matches in case it has changed.
+    let visibleMatchCount = countVisibleMatches_(null);
 
-  // Get string consisting of the text contents of the match nodes and the
-  // nodes before and after them, if applicable.
-  // This will be read out as an accessibility notification for the match.
-  // The siblings's textContent are added into the node array, because the
-  // nextSibling and previousSibling properties to the match nodes sometimes
-  // are text nodes, not HTML nodes. This results in '[object Text]' string
-  // being added to the array instead of the object.
+    if (visibleMatchCount == 0) {
+      selectedMatchIndex_ = -1;
+      selectedVisibleMatchIndex_ = -1;
+      return {matches: visibleMatchCount, index: -1};
+    }
 
-  let contextString = '';
-  const firstNode = match.nodes[0];
-  if (firstNode && firstNode.previousSibling) {
-    contextString += firstNode.previousSibling.textContent;
-  }
-  contextString += match.nodes
-                       .map(function(node) {
-                         if (node.textContent) {
-                           return node.textContent;
-                         } else {
-                           return node;
-                         }
-                       })
-                       .join('');
+    if (index >= visibleMatchCount) {
+      // There are no longer that many visible matches.
+      // Select the last match if moving to previous match.
+      // Select the first currently visible match if moving to next match.
+      index = index > selectedVisibleMatchIndex_ ? 0 : visibleMatchCount - 1;
+    }
 
-  const lastNode = match.nodes[match.nodes.length - 1];
-  if (lastNode && lastNode.nextSibling) {
-    contextString += lastNode.nextSibling.textContent;
-  }
+    let total_match_index = 0;
+    var visible_match_count = index;
+    // Select the `index`-th visible match.
+    while (total_match_index < gCrWeb.findInPage.matches.length) {
+      if (gCrWeb.findInPage.matches[total_match_index].visible()) {
+        visible_match_count--;
+        if (visible_match_count < 0) {
+          break;
+        }
+      }
+      total_match_index++;
+    }
 
-  return {
-    matches: visibleMatchCount,
-    index: index,
-    contextString: contextString
-  };
+    selectedMatchIndex_ = total_match_index;
+    selectedVisibleMatchIndex_ = index;
+
+    match = getCurrentSelectedMatch_();
+    if (!match) {
+      return {matches: visibleMatchCount, index: -1};
+    }
+    match.addSelectHighlight();
+    scrollToCurrentlySelectedMatch_();
+
+    // Get string consisting of the text contents of the match nodes and the
+    // nodes before and after them, if applicable.
+    // This will be read out as an accessibility notification for the match.
+    // The siblings's textContent are added into the node array, because the
+    // nextSibling and previousSibling properties to the match nodes sometimes
+    // are text nodes, not HTML nodes. This results in '[object Text]' string
+    // being added to the array instead of the object.
+
+    let contextString = '';
+    const firstNode = match.nodes[0];
+    if (firstNode && firstNode.previousSibling) {
+      contextString += firstNode.previousSibling.textContent;
+    }
+    contextString += match.nodes
+                         .map(function(node) {
+                           if (node.textContent) {
+                             return node.textContent;
+                           } else {
+                             return node;
+                           }
+                         })
+                         .join('');
+
+    const lastNode = match.nodes[match.nodes.length - 1];
+    if (lastNode && lastNode.nextSibling) {
+      contextString += lastNode.nextSibling.textContent;
+    }
+
+    return {
+      matches: visibleMatchCount,
+      index: index,
+      contextString: contextString
+    };
+  });
 };
 
 /**
@@ -603,11 +608,13 @@
  * and class names.
  */
 function stop(): void {
-  if (styleElement_) {
-    removeStyle_();
-    cleanUp_();
-  }
-  gCrWeb.findInPage.hasInitialized = false;
+  catchAndReportErrors(function() {
+    if (styleElement_) {
+      removeStyle_();
+      cleanUp_();
+    }
+    gCrWeb.findInPage.hasInitialized = false;
+  });
 };
 
 // Mark: Public API
diff --git a/ios/web/js_features/context_menu/BUILD.gn b/ios/web/js_features/context_menu/BUILD.gn
index 68890f2..831dfcf 100644
--- a/ios/web/js_features/context_menu/BUILD.gn
+++ b/ios/web/js_features/context_menu/BUILD.gn
@@ -39,6 +39,7 @@
 
   deps = [
     ":compile_surrounding_text_ts",
+    "//ios/web/public/js_messaging:error_reporting",
     "//ios/web/public/js_messaging:gcrweb",
     "//ios/web/public/js_messaging:util_scripts",
   ]
@@ -49,7 +50,10 @@
 
   sources = [ "resources/main_frame_context_menu.ts" ]
 
-  deps = [ "//ios/web/public/js_messaging:gcrweb" ]
+  deps = [
+    "//ios/web/public/js_messaging:error_reporting",
+    "//ios/web/public/js_messaging:gcrweb",
+  ]
 }
 
 source_set("unittests") {
diff --git a/ios/web/js_features/context_menu/resources/all_frames_context_menu.ts b/ios/web/js_features/context_menu/resources/all_frames_context_menu.ts
index ad609d1..4af6c64 100644
--- a/ios/web/js_features/context_menu/resources/all_frames_context_menu.ts
+++ b/ios/web/js_features/context_menu/resources/all_frames_context_menu.ts
@@ -7,6 +7,7 @@
  */
 
 import {getSurroundingText} from '//ios/web/js_features/context_menu/resources/surrounding_text.js';
+import {catchAndReportErrors} from '//ios/web/public/js_messaging/resources/error_reporting.js';
 import {gCrWeb} from '//ios/web/public/js_messaging/resources/gcrweb.js';
 import {sendWebKitMessage} from '//ios/web/public/js_messaging/resources/utils.js'
 
@@ -211,28 +212,31 @@
  */
 function findElementAtPointInPageCoordinates(
     requestId: string, x: number, y: number) {
-  const hitCoordinates = spiralCoordinates(x, y);
-  const processedElements = new Set<Element>();
-  for (let coordinates of hitCoordinates) {
-    const coordinateDetails =
-        new WindowCoordinates(coordinates.x, coordinates.y);
-    const useViewPortCoordinates = elementFromPointIsUsingViewPortCoordinates();
-    const coordinateX = useViewPortCoordinates ? coordinateDetails.viewPortX :
-                                                 coordinateDetails.x;
-    const coordinateY = useViewPortCoordinates ? coordinateDetails.viewPortY :
-                                                 coordinateDetails.y;
-    const elementWasFound = findElementAtPoint(
-        requestId, window.document, processedElements, coordinateX, coordinateY,
-        x, y);
+  catchAndReportErrors(function() {
+    const hitCoordinates = spiralCoordinates(x, y);
+    const processedElements = new Set<Element>();
+    for (let coordinates of hitCoordinates) {
+      const coordinateDetails =
+          new WindowCoordinates(coordinates.x, coordinates.y);
+      const useViewPortCoordinates =
+          elementFromPointIsUsingViewPortCoordinates();
+      const coordinateX = useViewPortCoordinates ? coordinateDetails.viewPortX :
+                                                   coordinateDetails.x;
+      const coordinateY = useViewPortCoordinates ? coordinateDetails.viewPortY :
+                                                   coordinateDetails.y;
+      const elementWasFound = findElementAtPoint(
+          requestId, window.document, processedElements, coordinateX,
+          coordinateY, x, y);
 
-    // Exit early if an element was found.
-    if (elementWasFound) {
-      return;
+      // Exit early if an element was found.
+      if (elementWasFound) {
+        return;
+      }
     }
-  }
 
-  // If no element was found, send an empty response.
-  sendFindElementAtPointResponse(requestId, /*response=*/ {});
+    // If no element was found, send an empty response.
+    sendFindElementAtPointResponse(requestId, /*response=*/ {});
+  });
 }
 
 /**
@@ -628,16 +632,18 @@
  * Processes context menu messages received by the window.
  */
 window.addEventListener('message', function(message) {
-  const payload = message.data;
-  if (!payload || typeof payload !== 'object') {
-    return;
-  }
-  if (payload.hasOwnProperty('type') &&
-      payload.type === 'org.chromium.contextMenuMessage') {
-    findElementAtPointInPageCoordinates(
-        payload.requestId, payload.x + window.pageXOffset,
-        payload.y + window.pageYOffset);
-  }
+  catchAndReportErrors(function() {
+    const payload = message.data;
+    if (!payload || typeof payload !== 'object') {
+      return;
+    }
+    if (payload.hasOwnProperty('type') &&
+        payload.type === 'org.chromium.contextMenuMessage') {
+      findElementAtPointInPageCoordinates(
+          payload.requestId, payload.x + window.pageXOffset,
+          payload.y + window.pageYOffset);
+    }
+  });
 });
 
 // Call contextMenuAllFrames on gCrWeb directly to prevent code duplication
diff --git a/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts b/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts
index d8b25c4..01eadcf 100644
--- a/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts
+++ b/ios/web/js_features/context_menu/resources/main_frame_context_menu.ts
@@ -6,6 +6,7 @@
  * @fileoverview APIs used by CRWContextMenuController.
  */
 
+import {catchAndReportErrors} from '//ios/web/public/js_messaging/resources/error_reporting.js';
 import {gCrWeb} from '//ios/web/public/js_messaging/resources/gcrweb.js';
 
 /**
@@ -22,8 +23,10 @@
  *                 coordinates.
  */
 function findElementAtPoint(requestId: string, x: number, y: number) {
-  gCrWeb.contextMenuAllFrames.findElementAtPointInPageCoordinates(
-      requestId, x, y);
+  catchAndReportErrors(function() {
+    gCrWeb.contextMenuAllFrames.findElementAtPointInPageCoordinates(
+        requestId, x, y);
+  });
 }
 
 gCrWeb.contextMenu = {findElementAtPoint};
diff --git a/ios/web/js_features/fullscreen/BUILD.gn b/ios/web/js_features/fullscreen/BUILD.gn
index 7e2f104..4678b22 100644
--- a/ios/web/js_features/fullscreen/BUILD.gn
+++ b/ios/web/js_features/fullscreen/BUILD.gn
@@ -25,6 +25,7 @@
 
   sources = [ "resources/fullscreen.ts" ]
   deps = [
+    "//ios/web/public/js_messaging:error_reporting",
     "//ios/web/public/js_messaging:frame_id",
     "//ios/web/public/js_messaging:util_scripts",
   ]
diff --git a/ios/web/js_features/fullscreen/resources/fullscreen.ts b/ios/web/js_features/fullscreen/resources/fullscreen.ts
index b21b1be..18af7c36 100644
--- a/ios/web/js_features/fullscreen/resources/fullscreen.ts
+++ b/ios/web/js_features/fullscreen/resources/fullscreen.ts
@@ -6,6 +6,7 @@
  * @fileoverview Reports viewport details to the app.
  */
 
+import {catchAndReportErrors} from '//ios/web/public/js_messaging/resources/error_reporting.js';
 import { getFrameId } from '//ios/web/public/js_messaging/resources/frame_id.js';
 import { sendWebKitMessage } from '//ios/web/public/js_messaging/resources/utils.js'
 
@@ -13,15 +14,17 @@
  * Reads the viewport configuration and reports it back to the browser.
  */
 function reportViewportConfiguration() {
-  let viewportMeta = window.document.querySelector('meta[name = "viewport"]');
-  if (viewportMeta) {
-    let coverValue =
-        viewportMeta.getAttribute('content')?.includes('viewport-fit=cover');
-    sendWebKitMessage('FullscreenViewportHandler', {
-      'frame_id': getFrameId(),
-      'cover': coverValue,
-    });
-  }
+  catchAndReportErrors(function() {
+    let viewportMeta = window.document.querySelector('meta[name = "viewport"]');
+    if (viewportMeta) {
+      let coverValue =
+          viewportMeta.getAttribute('content')?.includes('viewport-fit=cover');
+      sendWebKitMessage('FullscreenViewportHandler', {
+        'frame_id': getFrameId(),
+        'cover': coverValue,
+      });
+    }
+  });
 }
 
 window.addEventListener('load', reportViewportConfiguration);
diff --git a/ios_internal b/ios_internal
index 7a2df94..4c39224 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 7a2df944059b4837fd4c2cfabe6b251b3c8c6b47
+Subproject commit 4c39224d86d58e8c00816ac9a937e81aba8a8e1b
diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc
index 4102f5e..79a2650 100644
--- a/media/audio/audio_manager_base.cc
+++ b/media/audio/audio_manager_base.cc
@@ -457,23 +457,28 @@
   auto dispatcher_params = std::make_unique<DispatcherParams>(
       params, output_params, output_device_id);
 
-  auto it = base::ranges::find_if(
-      output_dispatchers_,
-      [&](const std::unique_ptr<DispatcherParams>& dispatcher) {
-        // We will reuse the existing dispatcher when:
-        // 1) Unified IO is not used, input_params and output_params of the
-        //    existing dispatcher are the same as the requested dispatcher.
-        // 2) Unified IO is used, input_params and output_params of the existing
-        //    dispatcher are the same as the request dispatcher.
-        bool same_offload_mode = output_params.RequireOffload() ==
-                                 dispatcher->output_params.RequireOffload();
-        return params.Equals(dispatcher->input_params) &&
-               output_params.Equals(dispatcher->output_params) &&
-               output_device_id == dispatcher->output_device_id &&
-               same_offload_mode;
-      });
-  if (it != output_dispatchers_.end())
-    return (*it)->dispatcher->CreateStreamProxy();
+  // Do not reuse the output dispatcher if audio offload is requested.
+  // Their underlying audio client is configured differently to make
+  // it work with expected buffer size according to requested output
+  // param.
+  if (!output_params.RequireOffload()) {
+    auto it = base::ranges::find_if(
+        output_dispatchers_,
+        [&](const std::unique_ptr<DispatcherParams>& dispatcher) {
+          // We will reuse the existing dispatcher when:
+          // 1) Unified IO is not used, input_params and output_params of the
+          //    existing dispatcher are the same as the requested dispatcher.
+          // 2) Unified IO is used, input_params and output_params of the
+          // existing
+          //    dispatcher are the same as the request dispatcher.
+          return params.Equals(dispatcher->input_params) &&
+                 output_params.Equals(dispatcher->output_params) &&
+                 output_device_id == dispatcher->output_device_id;
+        });
+    if (it != output_dispatchers_.end()) {
+      return (*it)->dispatcher->CreateStreamProxy();
+    }
+  }
 
   const base::TimeDelta kCloseDelay = base::Seconds(kStreamCloseDelaySeconds);
   std::unique_ptr<AudioOutputDispatcher> dispatcher;
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index 12df231..79bcaa0 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -253,16 +253,18 @@
     if (enable_audio_offload_) {
       audio_client->GetBufferSize(&preferred_frames_per_buffer);
 
-      // if the granted buffer size is not the same as requested buffer size
-      // re-initializing audio bus.
+      // TODO(crbug.com/348468130) : Consider reinitializing `audio_bus_` and
+      // handling mismatch of `packet_size_frames_` and
+      // `preferred_frames_per_buffer`.
+      // If `packet_size_frames_` doesn't match the preferred size, fallback to
+      // not offloading. This might happen after a device change.
       if (packet_size_frames_ != preferred_frames_per_buffer) {
-        AudioParameters params = AudioParameters(params_);
-        params.set_frames_per_buffer(preferred_frames_per_buffer);
-        audio_bus_ = AudioBus::Create(params);
-        // For Offload case GetBuffer API requires the preferred buffer size to
-        // be used or it will fail.
-        packet_size_frames_ = preferred_frames_per_buffer;
-        packet_size_bytes_ = params.GetBytesPerBuffer(kSampleFormatS16);
+        SendLogMessage(
+            "%s => (INFO: Requested buffer size in frames mismatch. "
+            "Disable audio offload for the stream.",
+            __func__);
+        // Return here to allow falling back to non-offload mode.
+        return false;
       }
     } else {
       preferred_frames_per_buffer = AudioTimestampHelper::TimeToFrames(
diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc
index 675291d3..a44fbc63 100644
--- a/media/audio/win/audio_output_win_unittest.cc
+++ b/media/audio/win/audio_output_win_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include <windows.h>
 
 #include <mmsystem.h>
diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc
index f49f19d..f42bb29e 100644
--- a/media/base/audio_decoder_config.cc
+++ b/media/base/audio_decoder_config.cc
@@ -25,6 +25,14 @@
 AudioDecoderConfig::AudioDecoderConfig(const AudioDecoderConfig& other) =
     default;
 
+AudioDecoderConfig::AudioDecoderConfig(AudioDecoderConfig&& other) = default;
+
+AudioDecoderConfig& AudioDecoderConfig::operator=(
+    const AudioDecoderConfig& other) = default;
+
+AudioDecoderConfig& AudioDecoderConfig::operator=(AudioDecoderConfig&& other) =
+    default;
+
 void AudioDecoderConfig::Initialize(AudioCodec codec,
                                     SampleFormat sample_format,
                                     ChannelLayout channel_layout,
diff --git a/media/base/audio_decoder_config.h b/media/base/audio_decoder_config.h
index 1e14d2f..0169da0 100644
--- a/media/base/audio_decoder_config.h
+++ b/media/base/audio_decoder_config.h
@@ -34,6 +34,9 @@
                      EncryptionScheme encryption_scheme);
 
   AudioDecoderConfig(const AudioDecoderConfig& other);
+  AudioDecoderConfig(AudioDecoderConfig&& other);
+  AudioDecoderConfig& operator=(const AudioDecoderConfig& other);
+  AudioDecoderConfig& operator=(AudioDecoderConfig&& other);
 
   ~AudioDecoderConfig();
 
diff --git a/media/base/decoder_buffer.cc b/media/base/decoder_buffer.cc
index 2ca11028..dfc0069 100644
--- a/media/base/decoder_buffer.cc
+++ b/media/base/decoder_buffer.cc
@@ -12,33 +12,33 @@
 
 namespace media {
 
-DecoderBuffer::TimeInfo::TimeInfo() = default;
-DecoderBuffer::TimeInfo::~TimeInfo() = default;
-DecoderBuffer::TimeInfo::TimeInfo(const TimeInfo&) = default;
-DecoderBuffer::TimeInfo& DecoderBuffer::TimeInfo::operator=(const TimeInfo&) =
-    default;
+namespace {
 
-DecoderBuffer::DecoderBuffer(size_t size) : size_(size) {
-  Initialize();
-}
+template <class T>
+class ExternalSharedMemoryAdapter : public DecoderBuffer::ExternalMemory {
+ public:
+  explicit ExternalSharedMemoryAdapter(T mapping)
+      : mapping_(std::move(mapping)) {}
+
+  const base::span<const uint8_t> Span() const override {
+    return mapping_.template GetMemoryAsSpan<const uint8_t>();
+  }
+
+ private:
+  T mapping_;
+};
+
+}  // namespace
+
+DecoderBuffer::DecoderBuffer(size_t size)
+    : data_(base::HeapArray<uint8_t>::Uninit(size)), size_(size) {}
 
 DecoderBuffer::DecoderBuffer(base::span<const uint8_t> data)
-    : size_(data.size()) {
-  Initialize();
-  data_.copy_from(data);
-}
+    : data_(base::HeapArray<uint8_t>::CopiedFrom(data)), size_(data.size()) {}
 
 DecoderBuffer::DecoderBuffer(base::HeapArray<uint8_t> data)
     : data_(std::move(data)), size_(data_.size()) {}
 
-DecoderBuffer::DecoderBuffer(base::ReadOnlySharedMemoryMapping mapping,
-                             size_t size)
-    : size_(size), read_only_mapping_(std::move(mapping)) {}
-
-DecoderBuffer::DecoderBuffer(base::WritableSharedMemoryMapping mapping,
-                             size_t size)
-    : size_(size), writable_mapping_(std::move(mapping)) {}
-
 DecoderBuffer::DecoderBuffer(std::unique_ptr<ExternalMemory> external_memory)
     : size_(external_memory->Span().size()),
       external_memory_(std::move(external_memory)) {}
@@ -49,10 +49,6 @@
 
 DecoderBuffer::~DecoderBuffer() = default;
 
-void DecoderBuffer::Initialize() {
-  data_ = base::HeapArray<uint8_t>::Uninit(size_);
-}
-
 // static
 scoped_refptr<DecoderBuffer> DecoderBuffer::CopyFrom(
     base::span<const uint8_t> data) {
@@ -78,7 +74,11 @@
   if (!mapping.IsValid()) {
     return nullptr;
   }
-  return base::WrapRefCounted(new DecoderBuffer(std::move(mapping), size));
+
+  return FromExternalMemory(
+      std::make_unique<
+          ExternalSharedMemoryAdapter<base::WritableSharedMemoryMapping>>(
+          std::move(mapping)));
 }
 
 // static
@@ -93,7 +93,11 @@
   if (!mapping.IsValid()) {
     return nullptr;
   }
-  return base::WrapRefCounted(new DecoderBuffer(std::move(mapping), size));
+
+  return FromExternalMemory(
+      std::make_unique<
+          ExternalSharedMemoryAdapter<base::ReadOnlySharedMemoryMapping>>(
+          std::move(mapping)));
 }
 
 // static
@@ -133,23 +137,35 @@
 
 base::span<const uint8_t> DecoderBuffer::AsSpan() const {
   DCHECK(!end_of_stream());
-  if (read_only_mapping_.IsValid()) {
-    return read_only_mapping_.GetMemoryAsSpan<const uint8_t>().first(size_);
-  }
-  if (writable_mapping_.IsValid()) {
-    return writable_mapping_.GetMemoryAsSpan<const uint8_t>().first(size_);
-  }
   if (external_memory_) {
     return external_memory_->Span().first(size_);
   }
   return data_.first(size_);
 }
 
-DecoderBufferSideData& DecoderBuffer::WritableSideData() {
-  if (!side_data_.has_value()) {
-    side_data_.emplace();
+void DecoderBuffer::set_discard_padding(const DiscardPadding& discard_padding) {
+  DCHECK(!end_of_stream());
+  if (discard_padding.first.is_zero() && discard_padding.second.is_zero()) {
+    discard_padding_.reset();
+    return;
   }
-  return side_data_.value();
+  discard_padding_ = std::make_unique<DiscardPadding>(discard_padding);
+}
+
+DecoderBufferSideData& DecoderBuffer::WritableSideData() {
+  if (!has_side_data()) {
+    side_data_ = std::make_unique<DecoderBufferSideData>();
+  }
+  return *side_data_;
+}
+
+void DecoderBuffer::set_side_data(
+    std::optional<DecoderBufferSideData> side_data) {
+  if (!side_data) {
+    side_data_.reset();
+    return;
+  }
+  WritableSideData() = *side_data;
 }
 
 bool DecoderBuffer::MatchesMetadataForTesting(
@@ -200,18 +216,20 @@
 
   std::ostringstream s;
 
-  s << "{timestamp=" << time_info_.timestamp.InMicroseconds()
-    << " duration=" << time_info_.duration.InMicroseconds() << " size=" << size_
+  s << "{timestamp=" << timestamp_.InMicroseconds()
+    << " duration=" << duration_.InMicroseconds() << " size=" << size_
     << " is_key_frame=" << is_key_frame_
     << " encrypted=" << (decrypt_config_ != nullptr);
 
   if (verbose) {
-    s << " has_side_data=" << has_side_data() << " discard_padding (us)=("
-      << time_info_.discard_padding.first.InMicroseconds() << ", "
-      << time_info_.discard_padding.second.InMicroseconds() << ")";
-
-    if (decrypt_config_)
+    s << " has_side_data=" << has_side_data();
+    if (discard_padding_) {
+      s << " discard_padding (us)=(" << discard_padding_->first.InMicroseconds()
+        << ", " << discard_padding_->second.InMicroseconds() << ")";
+    }
+    if (decrypt_config_) {
       s << " decrypt_config=" << (*decrypt_config_);
+    }
   }
 
   s << "}";
@@ -221,7 +239,7 @@
 
 void DecoderBuffer::set_timestamp(base::TimeDelta timestamp) {
   DCHECK(!end_of_stream());
-  time_info_.timestamp = timestamp;
+  timestamp_ = timestamp;
 }
 
 size_t DecoderBuffer::GetMemoryUsage() const {
diff --git a/media/base/decoder_buffer.h b/media/base/decoder_buffer.h
index 960ff510..08140ec9 100644
--- a/media/base/decoder_buffer.h
+++ b/media/base/decoder_buffer.h
@@ -50,12 +50,9 @@
 
   using DiscardPadding = std::pair<base::TimeDelta, base::TimeDelta>;
 
+  // TODO(crbug.com/365814210): Remove this structure. It's barely used outside
+  // of unit tests.
   struct MEDIA_EXPORT TimeInfo {
-    TimeInfo();
-    ~TimeInfo();
-    TimeInfo(const TimeInfo&);
-    TimeInfo& operator=(const TimeInfo&);
-
     // Presentation time of the frame.
     base::TimeDelta timestamp;
 
@@ -123,23 +120,24 @@
   // Method to verify if subsamples of a DecoderBuffer match.
   static bool DoSubsamplesMatch(const DecoderBuffer& buffer);
 
-  const TimeInfo& time_info() const {
+  // TODO(crbug.com/365814210): Remove this method.
+  TimeInfo time_info() const {
     DCHECK(!end_of_stream());
-    return time_info_;
+    return {timestamp_, duration_, discard_padding()};
   }
 
   base::TimeDelta timestamp() const {
     DCHECK(!end_of_stream());
-    return time_info_.timestamp;
+    return timestamp_;
   }
 
-  // TODO(dalecurtis): This should be renamed at some point, but to avoid a yak
-  // shave keep as a virtual with hacker_style() for now.
+  // TODO(crbug.com/365814210): This should be renamed at some point, but to
+  // avoid a yak shave keep as a virtual with hacker_style() for now.
   virtual void set_timestamp(base::TimeDelta timestamp);
 
   base::TimeDelta duration() const {
     DCHECK(!end_of_stream());
-    return time_info_.duration;
+    return duration_;
   }
 
   void set_duration(base::TimeDelta duration) {
@@ -147,17 +145,14 @@
     DCHECK(duration == kNoTimestamp ||
            (duration >= base::TimeDelta() && duration != kInfiniteDuration))
         << duration.InSecondsF();
-    time_info_.duration = duration;
+    duration_ = duration;
   }
 
   // The pointer to the start of the buffer. Prefer to construct a span around
   // the buffer, such as `base::span(decoder_buffer)`.
+  // TODO(crbug.com/365814210): Remove in favor of AsSpan().
   const uint8_t* data() const {
     DCHECK(!end_of_stream());
-    if (read_only_mapping_.IsValid())
-      return read_only_mapping_.GetMemoryAs<const uint8_t>();
-    if (writable_mapping_.IsValid())
-      return writable_mapping_.GetMemoryAs<const uint8_t>();
     if (external_memory_)
       return external_memory_->Span().data();
     return data_.data();
@@ -173,33 +168,30 @@
 
   // Prefer writable_span(), though it should also be removed.
   //
-  // TODO(sandersd): Remove writable_data(). https://crbug.com/834088
+  // TODO(crbug.com/41383992): Remove writable_data().
   uint8_t* writable_data() const {
     DCHECK(!end_of_stream());
-    DCHECK(!read_only_mapping_.IsValid());
-    DCHECK(!writable_mapping_.IsValid());
     DCHECK(!external_memory_);
     return const_cast<uint8_t*>(data_.data());
   }
 
-  // TODO(sandersd): Remove writable_span(). https://crbug.com/834088
+  // TODO(crbug.com/41383992): Remove writable_span().
   base::span<uint8_t> writable_span() const {
     // TODO(crbug.com/40284755): `data_` should be converted to HeapArray, then
     // it can give out a span safely.
     return UNSAFE_TODO(base::span(writable_data(), size()));
   }
 
-  inline bool empty() const { return size() == 0u; }
+  bool empty() const { return size() == 0u; }
 
-  const DiscardPadding& discard_padding() const {
+  // TODO(crbug.com/365814210): Convert to const*.
+  DiscardPadding discard_padding() const {
     DCHECK(!end_of_stream());
-    return time_info_.discard_padding;
+    return discard_padding_ ? *discard_padding_ : DiscardPadding();
   }
 
-  void set_discard_padding(const DiscardPadding& discard_padding) {
-    DCHECK(!end_of_stream());
-    time_info_.discard_padding = discard_padding;
-  }
+  // TODO(crbug.com/365814210): This should be renamed to CamelCase().
+  void set_discard_padding(const DiscardPadding& discard_padding);
 
   // Returns DecryptConfig associated with |this|. Returns null if |this| is
   // not encrypted.
@@ -233,17 +225,18 @@
     is_key_frame_ = is_key_frame;
   }
 
-  bool has_side_data() const { return side_data_.has_value(); }
-  const std::optional<DecoderBufferSideData>& side_data() const {
-    return side_data_;
+  bool has_side_data() const { return !!side_data_; }
+
+  // TODO(crbug.com/365814210): Convert to const*.
+  std::optional<DecoderBufferSideData> side_data() const {
+    return side_data_ ? std::optional<DecoderBufferSideData>(*side_data_)
+                      : std::nullopt;
   }
 
   // TODO(b/331652782): integrate the setter function into the constructor to
   // make |side_data_| immutable.
   DecoderBufferSideData& WritableSideData();
-  void set_side_data(const std::optional<DecoderBufferSideData>& side_data) {
-    side_data_ = side_data;
-  }
+  void set_side_data(std::optional<DecoderBufferSideData> side_data);
 
   // Returns true if all fields in |buffer| matches this buffer including
   // |data_|.
@@ -267,11 +260,7 @@
   // default to false.
   explicit DecoderBuffer(base::span<const uint8_t> data);
 
-  DecoderBuffer(base::HeapArray<uint8_t> data);
-
-  DecoderBuffer(base::ReadOnlySharedMemoryMapping mapping, size_t size);
-
-  DecoderBuffer(base::WritableSharedMemoryMapping mapping, size_t size);
+  explicit DecoderBuffer(base::HeapArray<uint8_t> data);
 
   explicit DecoderBuffer(std::unique_ptr<ExternalMemory> external_memory);
 
@@ -280,36 +269,44 @@
   virtual ~DecoderBuffer();
 
   // Encoded data, if it is stored on the heap.
-  base::HeapArray<uint8_t> data_;
+  const base::HeapArray<uint8_t> data_;
 
  private:
-  // Constructor helper method for memory allocations.
-  void Initialize();
+  // ***************************************************************************
+  // WARNING: This is a highly allocated object. Care should be taken when
+  // adding any fields to make sure they are absolutely necessary. If a field
+  // must be added and can be optional, ensure it is heap allocated through the
+  // usage of something like std::unique_ptr.
+  // ***************************************************************************
 
-  TimeInfo time_info_;
+  // Presentation time of the frame.
+  base::TimeDelta timestamp_;
+
+  // Presentation duration of the frame.
+  base::TimeDelta duration_;
+
+  // Duration of (audio) samples from the beginning and end of this frame
+  // which should be discarded after decoding. A value of kInfiniteDuration
+  // for the first value indicates the entire frame should be discarded; the
+  // second value must be base::TimeDelta() in this case.
+  std::unique_ptr<DiscardPadding> discard_padding_;
 
   // Size of the encoded data.
-  size_t size_;
+  const size_t size_ = 0u;
 
   // Structured side data.
-  std::optional<DecoderBufferSideData> side_data_;
+  std::unique_ptr<DecoderBufferSideData> side_data_;
 
-  // Encoded data, if it is stored in a read-only shared memory mapping.
-  base::ReadOnlySharedMemoryMapping read_only_mapping_;
-
-  // Encoded data, if it is stored in a writable shared memory mapping.
-  base::WritableSharedMemoryMapping writable_mapping_;
-
-  std::unique_ptr<ExternalMemory> external_memory_;
+  const std::unique_ptr<ExternalMemory> external_memory_;
 
   // Encryption parameters for the encoded data.
   std::unique_ptr<DecryptConfig> decrypt_config_;
 
   // Whether the frame was marked as a keyframe in the container.
-  bool is_key_frame_ = false;
+  bool is_key_frame_ : 1 = false;
 
   // Whether the buffer represent the end of stream.
-  const bool is_end_of_stream_ = false;
+  const bool is_end_of_stream_ : 1 = false;
 };
 
 }  // namespace media
diff --git a/media/base/video_decoder_config.cc b/media/base/video_decoder_config.cc
index ee8c7a7..63d4ec68 100644
--- a/media/base/video_decoder_config.cc
+++ b/media/base/video_decoder_config.cc
@@ -43,6 +43,14 @@
 VideoDecoderConfig::VideoDecoderConfig(const VideoDecoderConfig& other) =
     default;
 
+VideoDecoderConfig::VideoDecoderConfig(VideoDecoderConfig&& other) = default;
+
+VideoDecoderConfig& VideoDecoderConfig::operator=(
+    const VideoDecoderConfig& other) = default;
+
+VideoDecoderConfig& VideoDecoderConfig::operator=(VideoDecoderConfig&& other) =
+    default;
+
 VideoDecoderConfig::~VideoDecoderConfig() = default;
 
 void VideoDecoderConfig::Initialize(VideoCodec codec,
diff --git a/media/base/video_decoder_config.h b/media/base/video_decoder_config.h
index 19f9006..58bf211 100644
--- a/media/base/video_decoder_config.h
+++ b/media/base/video_decoder_config.h
@@ -46,7 +46,11 @@
                      const gfx::Size& natural_size,
                      const std::vector<uint8_t>& extra_data,
                      EncryptionScheme encryption_scheme);
+
   VideoDecoderConfig(const VideoDecoderConfig& other);
+  VideoDecoderConfig(VideoDecoderConfig&& other);
+  VideoDecoderConfig& operator=(const VideoDecoderConfig& other);
+  VideoDecoderConfig& operator=(VideoDecoderConfig&& other);
 
   ~VideoDecoderConfig();
 
diff --git a/media/cdm/cenc_utils_fuzzertest.cc b/media/cdm/cenc_utils_fuzzertest.cc
index d03fae3..759dcc5 100644
--- a/media/cdm/cenc_utils_fuzzertest.cc
+++ b/media/cdm/cenc_utils_fuzzertest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include <stddef.h>
 #include <stdint.h>
 #include <vector>
diff --git a/media/cdm/cenc_utils_unittest.cc b/media/cdm/cenc_utils_unittest.cc
index f50f083..d8c90fa 100644
--- a/media/cdm/cenc_utils_unittest.cc
+++ b/media/cdm/cenc_utils_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/cdm/cenc_utils.h"
 
 #include <stddef.h>
diff --git a/media/filters/demuxer_manager.cc b/media/filters/demuxer_manager.cc
index 6baed6b..294df6f 100644
--- a/media/filters/demuxer_manager.cc
+++ b/media/filters/demuxer_manager.cc
@@ -748,15 +748,14 @@
   }
 
   for (const auto& track : tracks->tracks()) {
-    if (track->type() == MediaTrack::Type::kAudio) {
-      client_->AddAudioTrack(track->track_id().value(), track->label().value(),
-                             track->language().value(), track->enabled());
-    } else if (track->type() == MediaTrack::Type::kVideo) {
-      client_->AddVideoTrack(track->track_id().value(), track->label().value(),
-                             track->language().value(), track->enabled());
-    } else {
-      // Text tracks are not supported through this code path.
-      NOTREACHED_IN_MIGRATION();
+    switch (track->type()) {
+      case MediaTrack::Type::kAudio:
+      case MediaTrack::Type::kVideo:
+        client_->AddMediaTrack(*track);
+        break;
+      default:
+        // Text tracks are not supported through this code path.
+        break;
     }
   }
 }
diff --git a/media/filters/demuxer_manager.h b/media/filters/demuxer_manager.h
index ea197c1..839d317 100644
--- a/media/filters/demuxer_manager.h
+++ b/media/filters/demuxer_manager.h
@@ -78,14 +78,7 @@
     virtual bool IsSecurityOriginCryptographic() const = 0;
 
 #if BUILDFLAG(ENABLE_FFMPEG)
-    virtual void AddAudioTrack(const std::string& id,
-                               const std::string& label,
-                               const std::string& language,
-                               bool is_first_track) = 0;
-    virtual void AddVideoTrack(const std::string& id,
-                               const std::string& label,
-                               const std::string& language,
-                               bool is_first_track) = 0;
+    virtual void AddMediaTrack(const media::MediaTrack&) = 0;
 #endif  // BUILDFLAG(ENABLE_FFMPEG)
 
 #if BUILDFLAG(ENABLE_HLS_DEMUXER)
diff --git a/media/filters/ffmpeg_aac_bitstream_converter.cc b/media/filters/ffmpeg_aac_bitstream_converter.cc
index e26b6cd..ad9a8c82 100644
--- a/media/filters/ffmpeg_aac_bitstream_converter.cc
+++ b/media/filters/ffmpeg_aac_bitstream_converter.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/filters/ffmpeg_aac_bitstream_converter.h"
 
 #include "base/logging.h"
diff --git a/media/filters/ffmpeg_aac_bitstream_converter_unittest.cc b/media/filters/ffmpeg_aac_bitstream_converter_unittest.cc
index 1897eb09..093a5779 100644
--- a/media/filters/ffmpeg_aac_bitstream_converter_unittest.cc
+++ b/media/filters/ffmpeg_aac_bitstream_converter_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include <stddef.h>
 #include <stdint.h>
 
diff --git a/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc b/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc
index 8de8c46..cfbbb89 100644
--- a/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc
+++ b/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h"
 
 #include <stdint.h>
diff --git a/media/filters/h264_to_annex_b_bitstream_converter.cc b/media/filters/h264_to_annex_b_bitstream_converter.cc
index 74f552055..01069114 100644
--- a/media/filters/h264_to_annex_b_bitstream_converter.cc
+++ b/media/filters/h264_to_annex_b_bitstream_converter.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/filters/h264_to_annex_b_bitstream_converter.h"
 
 #include <stddef.h>
diff --git a/media/filters/h265_to_annex_b_bitstream_converter.cc b/media/filters/h265_to_annex_b_bitstream_converter.cc
index 640b481c..6bd4c8e 100644
--- a/media/filters/h265_to_annex_b_bitstream_converter.cc
+++ b/media/filters/h265_to_annex_b_bitstream_converter.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/filters/h265_to_annex_b_bitstream_converter.h"
 
 #include <stddef.h>
diff --git a/media/filters/hls_data_source_provider.cc b/media/filters/hls_data_source_provider.cc
index ad2b7fc68..11f4fb8 100644
--- a/media/filters/hls_data_source_provider.cc
+++ b/media/filters/hls_data_source_provider.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/filters/hls_data_source_provider.h"
 #include "base/trace_event/trace_event.h"
 
diff --git a/media/filters/hls_manifest_demuxer_engine.cc b/media/filters/hls_manifest_demuxer_engine.cc
index b933a3e..c8e0589 100644
--- a/media/filters/hls_manifest_demuxer_engine.cc
+++ b/media/filters/hls_manifest_demuxer_engine.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/filters/hls_manifest_demuxer_engine.h"
 
 #include <optional>
diff --git a/media/filters/hls_rendition_impl.cc b/media/filters/hls_rendition_impl.cc
index 02b897d..45dea50a 100644
--- a/media/filters/hls_rendition_impl.cc
+++ b/media/filters/hls_rendition_impl.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/filters/hls_rendition_impl.h"
 
 #include "base/task/bind_post_task.h"
diff --git a/media/formats/mp2t/descriptors.cc b/media/formats/mp2t/descriptors.cc
index 94c967fb..6c2eb9c 100644
--- a/media/formats/mp2t/descriptors.cc
+++ b/media/formats/mp2t/descriptors.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/descriptors.h"
 
 #include <vector>
diff --git a/media/formats/mp2t/es_adapter_video_unittest.cc b/media/formats/mp2t/es_adapter_video_unittest.cc
index a7d3be73..1882780 100644
--- a/media/formats/mp2t/es_adapter_video_unittest.cc
+++ b/media/formats/mp2t/es_adapter_video_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/es_adapter_video.h"
 
 #include <stddef.h>
diff --git a/media/formats/mp2t/es_parser.cc b/media/formats/mp2t/es_parser.cc
index 1bf00d39..b66f551 100644
--- a/media/formats/mp2t/es_parser.cc
+++ b/media/formats/mp2t/es_parser.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/es_parser.h"
 
 #include "base/logging.h"
diff --git a/media/formats/mp2t/es_parser_adts.cc b/media/formats/mp2t/es_parser_adts.cc
index e16cd9e..dea858d 100644
--- a/media/formats/mp2t/es_parser_adts.cc
+++ b/media/formats/mp2t/es_parser_adts.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/es_parser_adts.h"
 
 #include <stddef.h>
diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc
index b75fc95..1064355 100644
--- a/media/formats/mp2t/es_parser_h264.cc
+++ b/media/formats/mp2t/es_parser_h264.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/es_parser_h264.h"
 
 #include <limits>
diff --git a/media/formats/mp2t/es_parser_mpeg1audio.cc b/media/formats/mp2t/es_parser_mpeg1audio.cc
index 6cd645fd..597e7ca 100644
--- a/media/formats/mp2t/es_parser_mpeg1audio.cc
+++ b/media/formats/mp2t/es_parser_mpeg1audio.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/es_parser_mpeg1audio.h"
 
 #include <vector>
diff --git a/media/formats/mp2t/es_parser_test_base.cc b/media/formats/mp2t/es_parser_test_base.cc
index 414275e..acc95d3 100644
--- a/media/formats/mp2t/es_parser_test_base.cc
+++ b/media/formats/mp2t/es_parser_test_base.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/es_parser_test_base.h"
 
 #include "base/check_op.h"
diff --git a/media/formats/mp2t/mp2t_stream_parser.cc b/media/formats/mp2t/mp2t_stream_parser.cc
index e89c0a3c..ab1ad25 100644
--- a/media/formats/mp2t/mp2t_stream_parser.cc
+++ b/media/formats/mp2t/mp2t_stream_parser.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/mp2t_stream_parser.h"
 
 #include <memory>
diff --git a/media/formats/mp2t/mp2t_stream_parser_unittest.cc b/media/formats/mp2t/mp2t_stream_parser_unittest.cc
index 0d0411b..abfa59c 100644
--- a/media/formats/mp2t/mp2t_stream_parser_unittest.cc
+++ b/media/formats/mp2t/mp2t_stream_parser_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/mp2t_stream_parser.h"
 
 #include <openssl/aes.h>
diff --git a/media/formats/mp2t/timestamp_unroller_unittest.cc b/media/formats/mp2t/timestamp_unroller_unittest.cc
index cc2f999..98f3b1b5 100644
--- a/media/formats/mp2t/timestamp_unroller_unittest.cc
+++ b/media/formats/mp2t/timestamp_unroller_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/timestamp_unroller.h"
 
 #include <stddef.h>
diff --git a/media/formats/mp2t/ts_packet.cc b/media/formats/mp2t/ts_packet.cc
index 5a28e4d..79a5202a 100644
--- a/media/formats/mp2t/ts_packet.cc
+++ b/media/formats/mp2t/ts_packet.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/ts_packet.h"
 
 #include <memory>
diff --git a/media/formats/mp2t/ts_section_pes.cc b/media/formats/mp2t/ts_section_pes.cc
index e97a710a..bb3b8c399 100644
--- a/media/formats/mp2t/ts_section_pes.cc
+++ b/media/formats/mp2t/ts_section_pes.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/ts_section_pes.h"
 
 #include <memory>
diff --git a/media/formats/mp2t/ts_section_psi.cc b/media/formats/mp2t/ts_section_psi.cc
index ed2b03b..b951568d 100644
--- a/media/formats/mp2t/ts_section_psi.cc
+++ b/media/formats/mp2t/ts_section_psi.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp2t/ts_section_psi.h"
 
 #include <algorithm>
diff --git a/media/formats/mp4/aac.cc b/media/formats/mp4/aac.cc
index eae8efdff..ae3c469 100644
--- a/media/formats/mp4/aac.cc
+++ b/media/formats/mp4/aac.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/aac.h"
 
 #include <stddef.h>
diff --git a/media/formats/mp4/aac_unittest.cc b/media/formats/mp4/aac_unittest.cc
index c73dd4f..d35680b0 100644
--- a/media/formats/mp4/aac_unittest.cc
+++ b/media/formats/mp4/aac_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include <stdint.h>
 
 #include <string>
diff --git a/media/formats/mp4/ac3.cc b/media/formats/mp4/ac3.cc
index e884cec..dd900cd 100644
--- a/media/formats/mp4/ac3.cc
+++ b/media/formats/mp4/ac3.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/ac3.h"
 
 #include <algorithm>
diff --git a/media/formats/mp4/avc.cc b/media/formats/mp4/avc.cc
index 87a9b69..4f1d37b 100644
--- a/media/formats/mp4/avc.cc
+++ b/media/formats/mp4/avc.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/avc.h"
 
 #include <memory>
diff --git a/media/formats/mp4/avc_unittest.cc b/media/formats/mp4/avc_unittest.cc
index f05d3991..4828cd0 100644
--- a/media/formats/mp4/avc_unittest.cc
+++ b/media/formats/mp4/avc_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/avc.h"
 
 #include <stddef.h>
diff --git a/media/formats/mp4/box_reader_unittest.cc b/media/formats/mp4/box_reader_unittest.cc
index 0676d98..50d7f64 100644
--- a/media/formats/mp4/box_reader_unittest.cc
+++ b/media/formats/mp4/box_reader_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/box_reader.h"
 
 #include <stdint.h>
diff --git a/media/formats/mp4/eac3.cc b/media/formats/mp4/eac3.cc
index 6998491..6b808282 100644
--- a/media/formats/mp4/eac3.cc
+++ b/media/formats/mp4/eac3.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/eac3.h"
 
 #include <algorithm>
diff --git a/media/formats/mp4/es_descriptor_unittest.cc b/media/formats/mp4/es_descriptor_unittest.cc
index 04e9eae..94bac2a 100644
--- a/media/formats/mp4/es_descriptor_unittest.cc
+++ b/media/formats/mp4/es_descriptor_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/es_descriptor.h"
 
 #include <stdint.h>
diff --git a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_fuzztest.cc b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_fuzztest.cc
index 5e7d0c44..28daaab 100644
--- a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_fuzztest.cc
+++ b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_fuzztest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include <stdint.h>
 
 #include <memory>
diff --git a/media/formats/mp4/hevc.cc b/media/formats/mp4/hevc.cc
index db812462..024aff9 100644
--- a/media/formats/mp4/hevc.cc
+++ b/media/formats/mp4/hevc.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/hevc.h"
 
 #include <algorithm>
diff --git a/media/formats/mp4/hevc_unittest.cc b/media/formats/mp4/hevc_unittest.cc
index 7920699..d9cdf73 100644
--- a/media/formats/mp4/hevc_unittest.cc
+++ b/media/formats/mp4/hevc_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/hevc.h"
 
 #include "media/formats/mp4/nalu_test_helper.h"
diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc
index 11924074..a5953048 100644
--- a/media/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/mp4_stream_parser.h"
 
 #include <stddef.h>
diff --git a/media/formats/mp4/sample_to_group_iterator_unittest.cc b/media/formats/mp4/sample_to_group_iterator_unittest.cc
index fa99954..3f94267 100644
--- a/media/formats/mp4/sample_to_group_iterator_unittest.cc
+++ b/media/formats/mp4/sample_to_group_iterator_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/sample_to_group_iterator.h"
 
 #include <stddef.h>
diff --git a/media/formats/mp4/track_run_iterator_unittest.cc b/media/formats/mp4/track_run_iterator_unittest.cc
index 6cd2646..22874d2 100644
--- a/media/formats/mp4/track_run_iterator_unittest.cc
+++ b/media/formats/mp4/track_run_iterator_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mp4/track_run_iterator.h"
 
 #include <stddef.h>
diff --git a/media/formats/mpeg/adts_stream_parser.cc b/media/formats/mpeg/adts_stream_parser.cc
index a406375..5e0b48bd 100644
--- a/media/formats/mpeg/adts_stream_parser.cc
+++ b/media/formats/mpeg/adts_stream_parser.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/formats/mpeg/adts_stream_parser.h"
 
 #include <stddef.h>
diff --git a/media/gpu/android/ndk_audio_encoder.cc b/media/gpu/android/ndk_audio_encoder.cc
index 26478c1a..6084e78 100644
--- a/media/gpu/android/ndk_audio_encoder.cc
+++ b/media/gpu/android/ndk_audio_encoder.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/android/ndk_audio_encoder.h"
 
 #include <aaudio/AAudio.h>
diff --git a/media/gpu/h265_builder.cc b/media/gpu/h265_builder.cc
index 44e716f9..a3b4864d4 100644
--- a/media/gpu/h265_builder.cc
+++ b/media/gpu/h265_builder.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/h265_builder.h"
 #include "media/filters/h26x_annex_b_bitstream_builder.h"
 
diff --git a/media/gpu/h265_decoder.cc b/media/gpu/h265_decoder.cc
index 72fd170a..69c5b6c 100644
--- a/media/gpu/h265_decoder.cc
+++ b/media/gpu/h265_decoder.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/h265_decoder.h"
 
 #include <algorithm>
diff --git a/media/gpu/h265_decoder_fuzzer.cc b/media/gpu/h265_decoder_fuzzer.cc
index 0e60dab..36f3866 100644
--- a/media/gpu/h265_decoder_fuzzer.cc
+++ b/media/gpu/h265_decoder_fuzzer.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/h265_decoder.h"
 
 #include <stddef.h>
diff --git a/media/gpu/mac/video_toolbox_h265_accelerator.cc b/media/gpu/mac/video_toolbox_h265_accelerator.cc
index b8c3a9d..7efe77b 100644
--- a/media/gpu/mac/video_toolbox_h265_accelerator.cc
+++ b/media/gpu/mac/video_toolbox_h265_accelerator.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/mac/video_toolbox_h265_accelerator.h"
 
 #include <array>
diff --git a/media/gpu/v4l2/test/vp8_decoder.cc b/media/gpu/v4l2/test/vp8_decoder.cc
index c7e6af41..278727df 100644
--- a/media/gpu/v4l2/test/vp8_decoder.cc
+++ b/media/gpu/v4l2/test/vp8_decoder.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/v4l2/test/vp8_decoder.h"
 
 #include <linux/v4l2-controls.h>
diff --git a/media/gpu/v4l2/test/vp8_decoder.h b/media/gpu/v4l2/test/vp8_decoder.h
index 321114c..a9b9e6ac 100644
--- a/media/gpu/v4l2/test/vp8_decoder.h
+++ b/media/gpu/v4l2/test/vp8_decoder.h
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #ifndef MEDIA_GPU_V4L2_TEST_VP8_DECODER_H_
 #define MEDIA_GPU_V4L2_TEST_VP8_DECODER_H_
 
diff --git a/media/gpu/v4l2/test/vp9_decoder.cc b/media/gpu/v4l2/test/vp9_decoder.cc
index 14b99e6a..219fe63 100644
--- a/media/gpu/v4l2/test/vp9_decoder.cc
+++ b/media/gpu/v4l2/test/vp9_decoder.cc
@@ -418,7 +418,7 @@
                                                  std::vector<uint8_t>& v_plane,
                                                  gfx::Size& size,
                                                  BitDepth& bit_depth) {
-  Vp9FrameHeader frame_hdr{};
+  Vp9FrameHeader frame_hdr;
 
   Vp9Parser::Result parser_res = ReadNextFrame(frame_hdr, size);
   switch (parser_res) {
diff --git a/media/gpu/v4l2/v4l2_vp9_helpers_unittest.cc b/media/gpu/v4l2/v4l2_vp9_helpers_unittest.cc
index 204cf3b..6dd6910b 100644
--- a/media/gpu/v4l2/v4l2_vp9_helpers_unittest.cc
+++ b/media/gpu/v4l2/v4l2_vp9_helpers_unittest.cc
@@ -113,7 +113,7 @@
 
     // Parse the merged buffer with the created superframe index.
     for (size_t j = 0; j <= i; j++) {
-      Vp9FrameHeader frame_header{};
+      Vp9FrameHeader frame_header;
       gfx::Size allocate_size;
       std::unique_ptr<DecryptConfig> frame_decrypt_config;
       EXPECT_EQ(vp9_parser.ParseNextFrame(&frame_header, &allocate_size,
diff --git a/media/gpu/vaapi/h265_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/h265_vaapi_video_decoder_delegate.cc
index 0bfc9ee1..0b8c62308 100644
--- a/media/gpu/vaapi/h265_vaapi_video_decoder_delegate.cc
+++ b/media/gpu/vaapi/h265_vaapi_video_decoder_delegate.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/vaapi/h265_vaapi_video_decoder_delegate.h"
 
 #include "build/chromeos_buildflags.h"
diff --git a/media/gpu/vaapi/test/h265_decoder.cc b/media/gpu/vaapi/test/h265_decoder.cc
index 3dd032f0..0c630cc 100644
--- a/media/gpu/vaapi/test/h265_decoder.cc
+++ b/media/gpu/vaapi/test/h265_decoder.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/vaapi/test/h265_decoder.h"
 
 #include <algorithm>
diff --git a/media/gpu/vaapi/test/h265_vaapi_wrapper.cc b/media/gpu/vaapi/test/h265_vaapi_wrapper.cc
index 198c90a..8c30e20 100644
--- a/media/gpu/vaapi/test/h265_vaapi_wrapper.cc
+++ b/media/gpu/vaapi/test/h265_vaapi_wrapper.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/vaapi/test/h265_vaapi_wrapper.h"
 
 #include "build/chromeos_buildflags.h"
diff --git a/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc b/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc
index a9e45fbc..b046507 100644
--- a/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc
+++ b/media/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc
@@ -648,7 +648,7 @@
     const bool keyframe) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  Vp9FrameHeader hdr{};
+  Vp9FrameHeader hdr;
   DCHECK(!visible_size_.IsEmpty());
   hdr.frame_width = visible_size_.width();
   hdr.frame_height = visible_size_.height();
diff --git a/media/gpu/windows/d3d11_h265_accelerator.cc b/media/gpu/windows/d3d11_h265_accelerator.cc
index a5892d2..c9a7c3c 100644
--- a/media/gpu/windows/d3d11_h265_accelerator.cc
+++ b/media/gpu/windows/d3d11_h265_accelerator.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/gpu/windows/d3d11_h265_accelerator.h"
 
 #include <algorithm>
diff --git a/media/mojo/mojom/stable/stable_video_decoder_types_mojom_traits.cc b/media/mojo/mojom/stable/stable_video_decoder_types_mojom_traits.cc
index 32e9116..c9c03a5 100644
--- a/media/mojo/mojom/stable/stable_video_decoder_types_mojom_traits.cc
+++ b/media/mojo/mojom/stable/stable_video_decoder_types_mojom_traits.cc
@@ -372,7 +372,7 @@
     front_discard(const scoped_refptr<media::DecoderBuffer>& input) {
   static_assert(
       std::is_same<decltype(input->discard_padding()),
-                   const std::pair<base::TimeDelta, base::TimeDelta>&>::value,
+                   std::pair<base::TimeDelta, base::TimeDelta>>::value,
       "Unexpected type for input->discard_padding(). If you need to change "
       "this assertion, please contact chromeos-gfx-video@google.com.");
   static_assert(
@@ -392,7 +392,7 @@
     back_discard(const scoped_refptr<media::DecoderBuffer>& input) {
   static_assert(
       std::is_same<decltype(input->discard_padding()),
-                   const std::pair<base::TimeDelta, base::TimeDelta>&>::value,
+                   std::pair<base::TimeDelta, base::TimeDelta>>::value,
       "Unexpected type for input->discard_padding(). If you need to change "
       "this assertion, please contact chromeos-gfx-video@google.com.");
   static_assert(
@@ -413,7 +413,7 @@
     side_data(const scoped_refptr<media::DecoderBuffer>& input) {
   static_assert(
       std::is_same<decltype(input->side_data()),
-                   const std::optional<media::DecoderBufferSideData>&>::value,
+                   std::optional<media::DecoderBufferSideData>>::value,
       "Unexpected type for input->side_data(). If you need to change this "
       "assertion, please contact chromeos-gfx-video@google.com.");
   if (input->end_of_stream()) {
diff --git a/media/muxers/mp4_muxer_delegate.cc b/media/muxers/mp4_muxer_delegate.cc
index 299f329..ecd7437 100644
--- a/media/muxers/mp4_muxer_delegate.cc
+++ b/media/muxers/mp4_muxer_delegate.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/muxers/mp4_muxer_delegate.h"
 
 #include "base/logging.h"
diff --git a/media/muxers/mp4_muxer_delegate_unittest.cc b/media/muxers/mp4_muxer_delegate_unittest.cc
index 89a51ed..acc1553c 100644
--- a/media/muxers/mp4_muxer_delegate_unittest.cc
+++ b/media/muxers/mp4_muxer_delegate_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/muxers/mp4_muxer_delegate.h"
 
 #include <algorithm>
diff --git a/media/parsers/h264_bit_reader.cc b/media/parsers/h264_bit_reader.cc
index fd77d320..81a5178 100644
--- a/media/parsers/h264_bit_reader.cc
+++ b/media/parsers/h264_bit_reader.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/parsers/h264_bit_reader.h"
 #include "base/check.h"
 
diff --git a/media/parsers/h265_nalu_parser.cc b/media/parsers/h265_nalu_parser.cc
index 8dcf4cf..0eaa1f4 100644
--- a/media/parsers/h265_nalu_parser.cc
+++ b/media/parsers/h265_nalu_parser.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/parsers/h265_nalu_parser.h"
 
 #include <stddef.h>
diff --git a/media/parsers/h265_parser.cc b/media/parsers/h265_parser.cc
index 4930c80..f91ea30 100644
--- a/media/parsers/h265_parser.cc
+++ b/media/parsers/h265_parser.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/parsers/h265_parser.h"
 
 #include <stddef.h>
diff --git a/media/parsers/h265_parser_unittest.cc b/media/parsers/h265_parser_unittest.cc
index d80bb66..06d35226 100644
--- a/media/parsers/h265_parser_unittest.cc
+++ b/media/parsers/h265_parser_unittest.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include <memory>
 #include <string>
 
diff --git a/media/parsers/vp9_parser.cc b/media/parsers/vp9_parser.cc
index b69dd41d..93ba116c 100644
--- a/media/parsers/vp9_parser.cc
+++ b/media/parsers/vp9_parser.cc
@@ -287,6 +287,13 @@
 
 }  // namespace
 
+Vp9FrameHeader::Vp9FrameHeader() = default;
+Vp9FrameHeader::Vp9FrameHeader(const Vp9FrameHeader&) = default;
+Vp9FrameHeader::Vp9FrameHeader(Vp9FrameHeader&&) = default;
+Vp9FrameHeader& Vp9FrameHeader::operator=(const Vp9FrameHeader&) = default;
+Vp9FrameHeader& Vp9FrameHeader::operator=(Vp9FrameHeader&&) = default;
+Vp9FrameHeader::~Vp9FrameHeader() = default;
+
 bool Vp9FrameHeader::IsKeyframe() const {
   // When show_existing_frame is true, the frame header does not precede an
   // actual frame to be decoded, so frame_type does not apply (and is not read
diff --git a/media/parsers/vp9_parser.h b/media/parsers/vp9_parser.h
index 5b40ea8..d0131c90 100644
--- a/media/parsers/vp9_parser.h
+++ b/media/parsers/vp9_parser.h
@@ -197,6 +197,13 @@
     INTERFRAME = 1,
   };
 
+  Vp9FrameHeader();
+  Vp9FrameHeader(const Vp9FrameHeader&);
+  Vp9FrameHeader(Vp9FrameHeader&&);
+  Vp9FrameHeader& operator=(const Vp9FrameHeader&);
+  Vp9FrameHeader& operator=(Vp9FrameHeader&&);
+  ~Vp9FrameHeader();
+
   bool IsKeyframe() const;
   bool IsIntra() const;
   bool RefreshFlag(size_t i) const {
@@ -204,51 +211,51 @@
   }
   VideoColorSpace GetColorSpace() const;
 
-  uint8_t profile;
+  uint8_t profile = 0;
 
-  bool show_existing_frame;
-  uint8_t frame_to_show_map_idx;
+  bool show_existing_frame = false;
+  uint8_t frame_to_show_map_idx = 0;
 
-  FrameType frame_type;
+  FrameType frame_type{KEYFRAME};
 
-  bool show_frame;
-  bool error_resilient_mode;
+  bool show_frame = false;
+  bool error_resilient_mode = false;
 
-  uint8_t bit_depth;
-  Vp9ColorSpace color_space;
-  bool color_range;
-  uint8_t subsampling_x;
-  uint8_t subsampling_y;
+  uint8_t bit_depth = 0;
+  Vp9ColorSpace color_space{Vp9ColorSpace::UNKNOWN};
+  bool color_range = false;
+  uint8_t subsampling_x = 0;
+  uint8_t subsampling_y = 0;
 
   // The range of frame_width and frame_height is 1..2^16.
-  uint32_t frame_width;
-  uint32_t frame_height;
-  uint32_t render_width;
-  uint32_t render_height;
+  uint32_t frame_width = 0;
+  uint32_t frame_height = 0;
+  uint32_t render_width = 0;
+  uint32_t render_height = 0;
 
-  bool intra_only;
-  uint8_t reset_frame_context;
-  uint8_t refresh_frame_flags;
-  uint8_t ref_frame_idx[kVp9NumRefsPerFrame];
-  bool ref_frame_sign_bias[Vp9RefType::VP9_FRAME_MAX];
-  bool allow_high_precision_mv;
-  Vp9InterpolationFilter interpolation_filter;
+  bool intra_only = false;
+  uint8_t reset_frame_context = 0;
+  uint8_t refresh_frame_flags = 0;
+  uint8_t ref_frame_idx[kVp9NumRefsPerFrame] = {0};
+  bool ref_frame_sign_bias[Vp9RefType::VP9_FRAME_MAX] = {false};
+  bool allow_high_precision_mv = false;
+  Vp9InterpolationFilter interpolation_filter{Vp9InterpolationFilter::EIGHTTAP};
 
-  bool refresh_frame_context;
-  bool frame_parallel_decoding_mode;
-  uint8_t frame_context_idx;
+  bool refresh_frame_context = false;
+  bool frame_parallel_decoding_mode = false;
+  uint8_t frame_context_idx = 0;
   // |frame_context_idx_to_save_probs| is to be used by save_probs() only, and
   // |frame_context_idx| otherwise.
-  uint8_t frame_context_idx_to_save_probs;
+  uint8_t frame_context_idx_to_save_probs = 0;
 
-  Vp9QuantizationParams quant_params;
+  Vp9QuantizationParams quant_params = {};
 
-  uint8_t tile_cols_log2;
-  uint8_t tile_rows_log2;
+  uint8_t tile_cols_log2 = 0;
+  uint8_t tile_rows_log2 = 0;
 
-  // Pointer to the beginning of frame data. It is a responsibility of the
-  // client of the Vp9Parser to maintain validity of this data while it is
-  // being used outside of that class.
+  // Frame data. It is a responsibility of the client of the Vp9Parser to
+  // maintain validity of this data while it is being used outside of that
+  // class.
   // RAW_PTR_EXCLUSION: Rewriting causes unrelated test failures.
   // TODO(crbug.com/349424269): Fix tests and rewrite.
   RAW_PTR_EXCLUSION const uint8_t* data;
@@ -257,19 +264,19 @@
   size_t frame_size;
 
   // Size of compressed header in bytes.
-  size_t header_size_in_bytes;
+  size_t header_size_in_bytes = 0;
 
   // Size of uncompressed header in bytes.
-  size_t uncompressed_header_size;
+  size_t uncompressed_header_size = 0;
 
-  Vp9CompressedHeader compressed_header;
+  Vp9CompressedHeader compressed_header = {};
 
   // Current frame entropy context after header parsing.
-  Vp9FrameContext frame_context;
+  Vp9FrameContext frame_context = {};
 
   // Segmentation and loop filter params from uncompressed header
-  Vp9SegmentationParams segmentation;
-  Vp9LoopFilterParams loop_filter;
+  Vp9SegmentationParams segmentation = {};
+  Vp9LoopFilterParams loop_filter = {};
 };
 
 // A parser for VP9 bitstream.
diff --git a/media/parsers/vp9_parser_unittest.cc b/media/parsers/vp9_parser_unittest.cc
index e03cd4cc..ea20503 100644
--- a/media/parsers/vp9_parser_unittest.cc
+++ b/media/parsers/vp9_parser_unittest.cc
@@ -669,7 +669,7 @@
 INSTANTIATE_TEST_SUITE_P(All, Vp9ParserTest, ::testing::ValuesIn(kTestParams));
 
 TEST_F(Vp9ParserTest, CheckColorSpace) {
-  Vp9FrameHeader fhdr{};
+  Vp9FrameHeader fhdr;
   EXPECT_FALSE(fhdr.GetColorSpace().IsSpecified());
   fhdr.color_space = Vp9ColorSpace::BT_709;
   EXPECT_EQ(VideoColorSpace::REC709(), fhdr.GetColorSpace());
diff --git a/media/parsers/vp9_uncompressed_header_parser_unittest.cc b/media/parsers/vp9_uncompressed_header_parser_unittest.cc
index 70c58bf..26c1aaa 100644
--- a/media/parsers/vp9_uncompressed_header_parser_unittest.cc
+++ b/media/parsers/vp9_uncompressed_header_parser_unittest.cc
@@ -38,7 +38,7 @@
 };
 
 TEST_F(Vp9UncompressedHeaderParserTest, SetupPastIndependence) {
-  Vp9FrameHeader frame_header = {};
+  Vp9FrameHeader frame_header;
 
   SetupPastIndependence(&frame_header);
 
diff --git a/media/renderers/win/media_foundation_audio_stream.cc b/media/renderers/win/media_foundation_audio_stream.cc
index 0426c98..06ed8aa3 100644
--- a/media/renderers/win/media_foundation_audio_stream.cc
+++ b/media/renderers/win/media_foundation_audio_stream.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/renderers/win/media_foundation_audio_stream.h"
 
 #include <mferror.h>
diff --git a/media/video/openh264_video_encoder.cc b/media/video/openh264_video_encoder.cc
index 7b032065..a92ddad 100644
--- a/media/video/openh264_video_encoder.cc
+++ b/media/video/openh264_video_encoder.cc
@@ -2,6 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef UNSAFE_BUFFERS_BUILD
+// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
+#pragma allow_unsafe_buffers
+#endif
+
 #include "media/video/openh264_video_encoder.h"
 
 #include <algorithm>
diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h
index 0173f9b1..7962a863 100644
--- a/mojo/public/cpp/bindings/sync_call_restrictions.h
+++ b/mojo/public/cpp/bindings/sync_call_restrictions.h
@@ -160,9 +160,7 @@
     ~ScopedAllowSyncCall() { DecreaseScopedAllowCount(); }
 
    private:
-#if ENABLE_SYNC_CALL_RESTRICTIONS
     base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait_;
-#endif
   };
 };
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 752ca04..e229ec4 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -106,6 +106,7 @@
     "CHROME_ROOT_STORE_ONLY=$chrome_root_store_only",
     "CHROME_ROOT_STORE_SUPPORTED=$chrome_root_store_supported",
     "ENABLE_DEVICE_BOUND_SESSIONS=$enable_device_bound_sessions",
+    "ENABLE_BRACKETED_PROXY_URIS=$enable_bracketed_proxy_uris",
   ]
 }
 
diff --git a/net/base/proxy_chain.cc b/net/base/proxy_chain.cc
index 1e0dc6f..c7702b5 100644
--- a/net/base/proxy_chain.cc
+++ b/net/base/proxy_chain.cc
@@ -12,8 +12,10 @@
 #include "base/pickle.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/stringprintf.h"
+#include "build/buildflag.h"
 #include "net/base/proxy_server.h"
 #include "net/base/proxy_string_util.h"
+#include "net/net_buildflags.h"
 
 namespace net {
 
@@ -157,6 +159,14 @@
   }
   DCHECK(is_multi_proxy());
 
+#if !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  // A chain can only be multi-proxy in release builds if it is for ip
+  // protection.
+  if (!is_for_ip_protection() && is_multi_proxy()) {
+    return false;
+  }
+#endif  // !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+
   // Verify that the chain is zero or more SCHEME_QUIC servers followed by zero
   // or more SCHEME_HTTPS servers.
   bool seen_quic = false;
diff --git a/net/base/proxy_chain_unittest.cc b/net/base/proxy_chain_unittest.cc
index f375d1e..5385aed3 100644
--- a/net/base/proxy_chain_unittest.cc
+++ b/net/base/proxy_chain_unittest.cc
@@ -15,8 +15,10 @@
 #include "base/pickle.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/gtest_util.h"
+#include "build/buildflag.h"
 #include "net/base/proxy_server.h"
 #include "net/base/proxy_string_util.h"
+#include "net/net_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -78,11 +80,6 @@
       ProxyChain(ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_SOCKS5));
   EXPECT_EQ(proxy_chain1.ToDebugString(), "[socks5://foo:333]");
 
-  ProxyChain proxy_chain2 =
-      ProxyChain({ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS),
-                  ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS)});
-  EXPECT_EQ(proxy_chain2.ToDebugString(), "[https://foo:444, https://foo:555]");
-
   ProxyChain direct_proxy_chain = ProxyChain::Direct();
   EXPECT_EQ(direct_proxy_chain.ToDebugString(), "[direct://]");
 
@@ -94,6 +91,15 @@
 
   ProxyChain invalid_proxy_chain = ProxyChain();
   EXPECT_EQ(invalid_proxy_chain.ToDebugString(), "INVALID PROXY CHAIN");
+
+// Multi-proxy chains can only be created outside of Ip Protection in debug
+// builds.
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  ProxyChain proxy_chain2 =
+      ProxyChain({ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS),
+                  ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS)});
+  EXPECT_EQ(proxy_chain2.ToDebugString(), "[https://foo:444, https://foo:555]");
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
 }
 
 TEST(ProxyChainTest, FromSchemeHostAndPort) {
@@ -219,28 +225,6 @@
   ASSERT_EQ(proxy.GetProxyServer(0), proxy_server);
 }
 
-TEST(ProxyChainTest, MultiProxyChain) {
-  auto proxy_server1 =
-      ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_HTTPS);
-  auto proxy_server2 =
-      ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS);
-  auto proxy_server3 =
-      ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS);
-
-  std::vector<ProxyServer> proxy_servers = {proxy_server1, proxy_server2,
-                                            proxy_server3};
-  auto proxy = ProxyChain(proxy_servers);
-
-  EXPECT_FALSE(proxy.is_direct());
-  EXPECT_FALSE(proxy.is_single_proxy());
-  EXPECT_TRUE(proxy.is_multi_proxy());
-  ASSERT_EQ(proxy.proxy_servers(), proxy_servers);
-  ASSERT_EQ(proxy.length(), 3u);
-  ASSERT_EQ(proxy.GetProxyServer(0), proxy_server1);
-  ASSERT_EQ(proxy.GetProxyServer(1), proxy_server2);
-  ASSERT_EQ(proxy.GetProxyServer(2), proxy_server3);
-}
-
 TEST(ProxyChainTest, SplitLast) {
   auto proxy_server1 =
       ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_HTTPS);
@@ -256,13 +240,16 @@
                 ProxyChain::ForIpProtection({proxy_server1, proxy_server2}),
                 proxy_server3));
 
-  auto chain2 = ProxyChain({proxy_server1, proxy_server2});
-  EXPECT_EQ(chain2.SplitLast(),
-            std::make_pair(ProxyChain({proxy_server1}), proxy_server2));
-
   auto chain1 = ProxyChain({proxy_server1});
   EXPECT_EQ(chain1.SplitLast(),
             std::make_pair(ProxyChain::Direct(), proxy_server1));
+
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  // Multi-proxy chains (not for Ip Protection) are only valid in debug builds.
+  auto chain2 = ProxyChain({proxy_server1, proxy_server2});
+  EXPECT_EQ(chain2.SplitLast(),
+            std::make_pair(ProxyChain({proxy_server1}), proxy_server2));
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
 }
 
 TEST(ProxyChainTest, Prefix) {
@@ -288,27 +275,35 @@
 TEST(ProxyChainTest, First) {
   auto proxy_server1 =
       ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_HTTPS);
+
+  auto chain = ProxyChain({proxy_server1});
+  EXPECT_EQ(chain.First(), proxy_server1);
+
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  // Multi-proxy chains (not for Ip Protection) are only valid in debug builds.
   auto proxy_server2 =
       ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS);
 
-  auto chain = ProxyChain({proxy_server1, proxy_server2});
+  chain = ProxyChain({proxy_server1, proxy_server2});
   EXPECT_EQ(chain.First(), proxy_server1);
-
-  chain = ProxyChain({proxy_server1});
-  EXPECT_EQ(chain.First(), proxy_server1);
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
 }
 
 TEST(ProxyChainTest, Last) {
   auto proxy_server1 =
       ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_HTTPS);
+
+  auto chain = ProxyChain({proxy_server1});
+  EXPECT_EQ(chain.Last(), proxy_server1);
+
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  // Multi-proxy chains (not for Ip Protection) are only valid in debug builds.
   auto proxy_server2 =
       ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS);
 
-  auto chain = ProxyChain({proxy_server1, proxy_server2});
+  chain = ProxyChain({proxy_server1, proxy_server2});
   EXPECT_EQ(chain.Last(), proxy_server2);
-
-  chain = ProxyChain({proxy_server1});
-  EXPECT_EQ(chain.Last(), proxy_server1);
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
 }
 
 TEST(ProxyChainTest, IsForIpProtection) {
@@ -338,34 +333,29 @@
   EXPECT_EQ(ip_protection_proxy_chain1.ip_protection_chain_id(),
             ProxyChain::kDefaultIpProtectionChainId);
 
-  auto regular_proxy_chain2 =
-      ProxyChain({ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS),
-                  ProxyUriToProxyServer("foo:666", ProxyServer::SCHEME_HTTPS)});
-  auto ip_protection_proxy_chain2 = ProxyChain::ForIpProtection(
-      {ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS),
-       ProxyUriToProxyServer("foo:666", ProxyServer::SCHEME_HTTPS)});
-  EXPECT_TRUE(ip_protection_proxy_chain2.is_for_ip_protection());
-  EXPECT_EQ(ip_protection_proxy_chain2.ip_protection_chain_id(),
-            ProxyChain::kDefaultIpProtectionChainId);
-  EXPECT_EQ(regular_proxy_chain2.proxy_servers(),
-            ip_protection_proxy_chain2.proxy_servers());
+  // Ensure that ProxyChain can be reassigned a new value created using its own
+  // `proxy_severs()`.
+  auto proxy_chain =
+      ProxyChain({ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS)});
+  auto copied_proxy_chain = proxy_chain;
 
-  auto self_assignable_proxy_chain =
-      ProxyChain({ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS),
-                  ProxyUriToProxyServer("foo:666", ProxyServer::SCHEME_HTTPS)});
-  auto copied_proxy_chain = self_assignable_proxy_chain;
-
-  EXPECT_FALSE(self_assignable_proxy_chain.is_for_ip_protection());
-  EXPECT_EQ(self_assignable_proxy_chain.ip_protection_chain_id(),
+  // Assert that the newly created `ProxyChain` is not for IP protection.
+  EXPECT_FALSE(proxy_chain.is_for_ip_protection());
+  EXPECT_EQ(proxy_chain.ip_protection_chain_id(),
             ProxyChain::kNotIpProtectionChainId);
 
-  self_assignable_proxy_chain = ProxyChain::ForIpProtection(
-      std::move(self_assignable_proxy_chain.proxy_servers()));
-  EXPECT_TRUE(self_assignable_proxy_chain.is_for_ip_protection());
-  EXPECT_EQ(self_assignable_proxy_chain.proxy_servers(),
-            copied_proxy_chain.proxy_servers());
-  EXPECT_EQ(self_assignable_proxy_chain.ip_protection_chain_id(),
+  // Re-assign new value to `proxy_chain` by using its own proxy servers to
+  // create a proxy chain for IP protection.
+  proxy_chain =
+      ProxyChain::ForIpProtection(std::move(proxy_chain.proxy_servers()));
+
+  // Assert re-assigned proxy chain is now for IP protection and contains the
+  // same servers from the original copy.
+  EXPECT_TRUE(proxy_chain.is_for_ip_protection());
+  EXPECT_EQ(proxy_chain.ip_protection_chain_id(),
             ProxyChain::kDefaultIpProtectionChainId);
+  EXPECT_FALSE(copied_proxy_chain.is_for_ip_protection());
+  EXPECT_EQ(proxy_chain.proxy_servers(), copied_proxy_chain.proxy_servers());
 
   auto chain_with_id = ProxyChain::ForIpProtection(
       {ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS),
@@ -403,34 +393,55 @@
   auto quic2 = ProxyUriToProxyServer("foo:777", ProxyServer::SCHEME_QUIC);
   auto socks = ProxyUriToProxyServer("foo:777", ProxyServer::SCHEME_SOCKS5);
 
+  // Single proxy chain is valid.
   EXPECT_TRUE(ProxyChain({https1}).IsValid());
+
+  // Invalid Chains. If multi-proxy chain support is disabled, any chain greater
+  // than length 1 is considered invalid. If support is enabled, these chains
+  // remain invalid due to the presence of quic or an invalid sequence of
+  // schemes.
+  //
+  // Contains QUIC --> Invalid.
   EXPECT_FALSE(ProxyChain({quic1}).IsValid());
-  EXPECT_TRUE(ProxyChain({https1, https2}).IsValid());
   EXPECT_FALSE(ProxyChain({quic1, https1}).IsValid());
   EXPECT_FALSE(ProxyChain({quic1, quic2, https1, https2}).IsValid());
   EXPECT_FALSE(ProxyChain({https1, quic2}).IsValid());
   EXPECT_FALSE(ProxyChain({https1, https2, quic1, quic2}).IsValid());
+  // ProxyChain cannot contains socks server. Only QUIC and HTTPS.
   EXPECT_FALSE(ProxyChain({socks, https1}).IsValid());
   EXPECT_FALSE(ProxyChain({socks, https1, https2}).IsValid());
   EXPECT_FALSE(ProxyChain({https1, socks}).IsValid());
   EXPECT_FALSE(ProxyChain({https1, https2, socks}).IsValid());
 
-  // IP protection accepts chains with SCHEME_QUIC, but CHECKs on failure
-  // instead of just creating an invalid chain.
-  auto IppChain = [](std::vector<ProxyServer> proxy_servers) {
-    return ProxyChain::ForIpProtection(std::move(proxy_servers));
-  };
-  EXPECT_TRUE(IppChain({https1}).IsValid());
-  EXPECT_TRUE(IppChain({quic1}).IsValid());
-  EXPECT_TRUE(IppChain({https1, https2}).IsValid());
-  EXPECT_TRUE(IppChain({quic1, https1}).IsValid());
-  EXPECT_TRUE(IppChain({quic1, quic2, https1, https2}).IsValid());
-  EXPECT_CHECK_DEATH(IppChain({https1, quic2}).IsValid());
-  EXPECT_CHECK_DEATH(IppChain({https1, https2, quic1, quic2}).IsValid());
-  EXPECT_CHECK_DEATH(IppChain({socks, https1}).IsValid());
-  EXPECT_CHECK_DEATH(IppChain({socks, https1, https2}).IsValid());
-  EXPECT_CHECK_DEATH(IppChain({https1, socks}).IsValid());
-  EXPECT_CHECK_DEATH(IppChain({https1, https2, socks}).IsValid());
+  // IP protection accepts chains with SCHEME_QUIC and/or multi-proxy chains
+  EXPECT_TRUE(ProxyChain::ForIpProtection({https1}).IsValid());
+  EXPECT_TRUE(ProxyChain::ForIpProtection({quic1}).IsValid());
+  EXPECT_TRUE(ProxyChain::ForIpProtection({https1, https2}).IsValid());
+  EXPECT_TRUE(ProxyChain::ForIpProtection({quic1, https1}).IsValid());
+  EXPECT_TRUE(
+      ProxyChain::ForIpProtection({quic1, quic2, https1, https2}).IsValid());
+
+  // IP protection CHECKs on failure instead of just creating an invalid chain.
+  // QUIC cannot follow HTTPS proxy server.
+  EXPECT_CHECK_DEATH(ProxyChain::ForIpProtection({https1, quic2}).IsValid());
+  EXPECT_CHECK_DEATH(
+      ProxyChain::ForIpProtection({https1, https2, quic1, quic2}).IsValid());
+  // Socks proxy server is not valid for multi-proxy chain.
+  EXPECT_CHECK_DEATH(ProxyChain::ForIpProtection({socks, https1}).IsValid());
+  EXPECT_CHECK_DEATH(
+      ProxyChain::ForIpProtection({socks, https1, https2}).IsValid());
+  EXPECT_CHECK_DEATH(ProxyChain::ForIpProtection({https1, socks}).IsValid());
+  EXPECT_CHECK_DEATH(
+      ProxyChain::ForIpProtection({https1, https2, socks}).IsValid());
+
+#if !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  bool multi_proxy_chain_supported = false;
+#else  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  bool multi_proxy_chain_supported = true;
+#endif
+  // Multi-proxy chains are only supported in debug mode.
+  EXPECT_EQ(ProxyChain({https1, https2}).IsValid(),
+            multi_proxy_chain_supported);
 }
 
 TEST(ProxyChainTest, Unequal) {
@@ -527,6 +538,75 @@
   EXPECT_EQ(proxy_chain, proxy_chain_from_pickle);
 }
 
+#if !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+// Multi-proxy chains that are not for Ip Protection are not allowed in release
+// builds. If created, it should be considered invalid.
+TEST(ProxyChainTest, MultiProxyChainNotForIpProtectionInvalidProxyChain) {
+  ProxyChain invalid_chain =
+      ProxyChain({ProxyUriToProxyServer("foo:11", ProxyServer::SCHEME_HTTPS),
+                  ProxyUriToProxyServer("hoo:11", ProxyServer::SCHEME_HTTPS)});
+
+  EXPECT_FALSE(invalid_chain.IsValid());
+}
+#else  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+TEST(ProxyChainTest, MultiProxyChain) {
+  auto proxy_server1 =
+      ProxyUriToProxyServer("foo:333", ProxyServer::SCHEME_HTTPS);
+  auto proxy_server2 =
+      ProxyUriToProxyServer("foo:444", ProxyServer::SCHEME_HTTPS);
+  auto proxy_server3 =
+      ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS);
+
+  std::vector<ProxyServer> proxy_servers = {proxy_server1, proxy_server2,
+                                            proxy_server3};
+  auto proxy = ProxyChain(proxy_servers);
+
+  EXPECT_FALSE(proxy.is_direct());
+  EXPECT_FALSE(proxy.is_single_proxy());
+  EXPECT_TRUE(proxy.is_multi_proxy());
+  ASSERT_EQ(proxy.proxy_servers(), proxy_servers);
+  ASSERT_EQ(proxy.length(), 3u);
+  ASSERT_EQ(proxy.GetProxyServer(0), proxy_server1);
+  ASSERT_EQ(proxy.GetProxyServer(1), proxy_server2);
+  ASSERT_EQ(proxy.GetProxyServer(2), proxy_server3);
+
+  // Ensure that proxy chains are equal even if one is for IP Protection.
+  auto regular_proxy_chain = ProxyChain({proxy_server1, proxy_server2});
+  auto ip_protection_proxy_chain =
+      ProxyChain::ForIpProtection({proxy_server1, proxy_server2});
+  EXPECT_TRUE(ip_protection_proxy_chain.is_for_ip_protection());
+  EXPECT_EQ(regular_proxy_chain.proxy_servers(),
+            ip_protection_proxy_chain.proxy_servers());
+}
+
+TEST(ProxyChainTest, MultiProxyChainsCanBeConvertedToForIpProtection) {
+  ProxyChain proxy_chain =
+      ProxyChain({ProxyUriToProxyServer("foo:555", ProxyServer::SCHEME_HTTPS),
+                  ProxyUriToProxyServer("foo:666", ProxyServer::SCHEME_HTTPS)});
+  ProxyChain copied_proxy_chain = proxy_chain;
+
+  // Assert the proxy chain is currently not for ip protection.
+  EXPECT_FALSE(proxy_chain.is_for_ip_protection());
+  EXPECT_EQ(proxy_chain.ip_protection_chain_id(),
+            ProxyChain::kNotIpProtectionChainId);
+
+  // Convert proxy_chain to be for IP protection.
+  proxy_chain =
+      ProxyChain::ForIpProtection(std::move(proxy_chain.proxy_servers()));
+
+  // Assert proxy_chain now shows it is for IP protection while copied proxy
+  // chain still isn't.
+  EXPECT_TRUE(proxy_chain.is_for_ip_protection());
+  EXPECT_EQ(proxy_chain.ip_protection_chain_id(),
+            ProxyChain::kDefaultIpProtectionChainId);
+  EXPECT_FALSE(copied_proxy_chain.is_for_ip_protection());
+  EXPECT_EQ(copied_proxy_chain.ip_protection_chain_id(),
+            ProxyChain::kNotIpProtectionChainId);
+
+  // Ensure servers contained are still equal.
+  EXPECT_EQ(proxy_chain.proxy_servers(), copied_proxy_chain.proxy_servers());
+}
+
 TEST(ProxyChainTest, PickleTwoProxies) {
   ProxyChain proxy_chain =
       ProxyChain({ProxyUriToProxyServer("foo:11", ProxyServer::SCHEME_HTTPS),
@@ -538,6 +618,7 @@
   proxy_chain_from_pickle.InitFromPickle(&iter);
   EXPECT_EQ(proxy_chain, proxy_chain_from_pickle);
 }
+#endif
 
 }  // namespace
 
diff --git a/net/base/proxy_string_util.cc b/net/base/proxy_string_util.cc
index 08f0a6b..bb2ca06 100644
--- a/net/base/proxy_string_util.cc
+++ b/net/base/proxy_string_util.cc
@@ -6,13 +6,18 @@
 
 #include <string>
 #include <string_view>
+#include <vector>
 
+#include "base/check.h"
 #include "base/notreached.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "build/buildflag.h"
 #include "net/base/proxy_server.h"
 #include "net/base/url_util.h"
 #include "net/http/http_util.h"
+#include "net/net_buildflags.h"
 #include "url/third_party/mozilla/url_parse.h"
 
 namespace net {
@@ -23,20 +28,24 @@
 // This mapping is case-insensitive. If no type could be matched
 // returns SCHEME_INVALID.
 ProxyServer::Scheme GetSchemeFromPacTypeInternal(std::string_view type) {
-  if (base::EqualsCaseInsensitiveASCII(type, "proxy"))
+  if (base::EqualsCaseInsensitiveASCII(type, "proxy")) {
     return ProxyServer::SCHEME_HTTP;
+  }
   if (base::EqualsCaseInsensitiveASCII(type, "socks")) {
     // Default to v4 for compatibility. This is because the SOCKS4 vs SOCKS5
     // notation didn't originally exist, so if a client returns SOCKS they
     // really meant SOCKS4.
     return ProxyServer::SCHEME_SOCKS4;
   }
-  if (base::EqualsCaseInsensitiveASCII(type, "socks4"))
+  if (base::EqualsCaseInsensitiveASCII(type, "socks4")) {
     return ProxyServer::SCHEME_SOCKS4;
-  if (base::EqualsCaseInsensitiveASCII(type, "socks5"))
+  }
+  if (base::EqualsCaseInsensitiveASCII(type, "socks5")) {
     return ProxyServer::SCHEME_SOCKS5;
-  if (base::EqualsCaseInsensitiveASCII(type, "https"))
+  }
+  if (base::EqualsCaseInsensitiveASCII(type, "https")) {
     return ProxyServer::SCHEME_HTTPS;
+  }
 
   return ProxyServer::SCHEME_INVALID;
 }
@@ -229,17 +238,65 @@
 }
 
 ProxyServer::Scheme GetSchemeFromUriScheme(std::string_view scheme) {
-  if (base::EqualsCaseInsensitiveASCII(scheme, "http"))
+  if (base::EqualsCaseInsensitiveASCII(scheme, "http")) {
     return ProxyServer::SCHEME_HTTP;
-  if (base::EqualsCaseInsensitiveASCII(scheme, "socks4"))
+  }
+  if (base::EqualsCaseInsensitiveASCII(scheme, "socks4")) {
     return ProxyServer::SCHEME_SOCKS4;
-  if (base::EqualsCaseInsensitiveASCII(scheme, "socks"))
+  }
+  if (base::EqualsCaseInsensitiveASCII(scheme, "socks")) {
     return ProxyServer::SCHEME_SOCKS5;
-  if (base::EqualsCaseInsensitiveASCII(scheme, "socks5"))
+  }
+  if (base::EqualsCaseInsensitiveASCII(scheme, "socks5")) {
     return ProxyServer::SCHEME_SOCKS5;
-  if (base::EqualsCaseInsensitiveASCII(scheme, "https"))
+  }
+  if (base::EqualsCaseInsensitiveASCII(scheme, "https")) {
     return ProxyServer::SCHEME_HTTPS;
+  }
   return ProxyServer::SCHEME_INVALID;
 }
 
+ProxyChain MultiProxyUrisToProxyChain(std::string_view uris,
+                                      ProxyServer::Scheme default_scheme) {
+#if !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  // This function should not be called in non-debug modes.
+  CHECK(false);
+#endif  // !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+
+  uris = HttpUtil::TrimLWS(uris);
+  if (uris.empty()) {
+    return ProxyChain();
+  }
+
+  bool has_multi_proxy_brackets = uris.front() == '[' && uris.back() == ']';
+  // Remove `[]` if present
+  if (has_multi_proxy_brackets) {
+    uris = HttpUtil::TrimLWS(uris.substr(1, uris.size() - 2));
+  }
+
+  std::vector<ProxyServer> proxy_server_list;
+  std::vector<std::string_view> uris_list = base::SplitStringPiece(
+      uris, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  size_t number_of_proxy_uris = uris_list.size();
+  bool has_invalid_format =
+      number_of_proxy_uris > 1 && !has_multi_proxy_brackets;
+
+  // If uris list is empty or has invalid formatting for multi-proxy chains, an
+  // invalid `ProxyChain` should be returned.
+  if (uris_list.empty() || has_invalid_format) {
+    return ProxyChain();
+  }
+
+  for (const auto& uri : uris_list) {
+    // If direct is found, it MUST be the only uri in the list. Otherwise, it is
+    // an invalid `ProxyChain()`.
+    if (base::EqualsCaseInsensitiveASCII(uri, "direct://")) {
+      return number_of_proxy_uris > 1 ? ProxyChain() : ProxyChain::Direct();
+    }
+
+    proxy_server_list.push_back(ProxyUriToProxyServer(uri, default_scheme));
+  }
+
+  return ProxyChain(std::move(proxy_server_list));
+}
 }  // namespace net
diff --git a/net/base/proxy_string_util.h b/net/base/proxy_string_util.h
index bde65af..4c5e5ec5 100644
--- a/net/base/proxy_string_util.h
+++ b/net/base/proxy_string_util.h
@@ -54,7 +54,7 @@
 NET_EXPORT std::string ProxyServerToPacResultElement(
     const ProxyServer& proxy_server);
 
-// Converts a non-standard URI string to/from a ProxyServer.
+// Converts a non-standard URI string to/from a ProxyChain.
 //
 // The non-standard URI strings have the format:
 //   [<scheme>"://"]<server>[":"<port>]
@@ -88,6 +88,27 @@
 //   "foopy:X"          INVALID -- bad port.
 NET_EXPORT ProxyChain ProxyUriToProxyChain(std::string_view uri,
                                            ProxyServer::Scheme default_scheme);
+
+// Converts a bracketed string of non-standard uris to a multi-proxy
+// `net::ProxyChain`.
+//
+// The `uris` parameter may contain 1 or more non-standard URIs but not 0 which
+// would result in an invalid `ProxyChain()`.
+//
+// If brackets are omitted from the `uris` string, it MUST be a single
+// non-standard URI. Otherwise, an invalid `ProxyChain()` will be returned.
+//
+//
+// The bracketed non-standard URIs strings have the format:
+//   [x y z] where individual non-standard uris are space delimited and
+//   encompassed within brackets.
+//   ex. [https://foopy:17 https://hoopy:17]
+//
+// Each non-standard URI string follows the format described in the
+// documentation for the `ProxyUriToProxyChain` function.
+NET_EXPORT ProxyChain
+MultiProxyUrisToProxyChain(std::string_view uris,
+                           ProxyServer::Scheme default_scheme);
 NET_EXPORT ProxyServer
 ProxyUriToProxyServer(std::string_view uri, ProxyServer::Scheme default_scheme);
 NET_EXPORT std::string ProxyServerToProxyUri(const ProxyServer& proxy_server);
diff --git a/net/base/proxy_string_util_unittest.cc b/net/base/proxy_string_util_unittest.cc
index ababf7f8..d067d35 100644
--- a/net/base/proxy_string_util_unittest.cc
+++ b/net/base/proxy_string_util_unittest.cc
@@ -4,8 +4,13 @@
 
 #include "net/base/proxy_string_util.h"
 
+#include <string>
+#include <vector>
+
+#include "build/buildflag.h"
 #include "net/base/proxy_chain.h"
 #include "net/base/proxy_server.h"
+#include "net/net_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -123,6 +128,25 @@
   EXPECT_FALSE(invalid_uri.is_direct());
 }
 
+// A multi-proxy string containing URIs is not acceptable input for the
+// ProxyUriToProxyChain function and should return an invalid `ProxyChain()`.
+TEST(ProxySpecificationUtilTest, ProxyUriToProxyChainWithBracketsInvalid) {
+  // Release builds should return an invalid proxy chain for multi-proxy chains.
+  const char* const invalid_multi_proxy_uris[] = {
+      "[]",
+      "[direct://]",
+      "[https://foopy]",
+      "[https://foopy https://hoopy]",
+  };
+
+  for (const char* uri : invalid_multi_proxy_uris) {
+    ProxyChain multi_proxy_uri =
+        ProxyUriToProxyChain(uri, ProxyServer::SCHEME_HTTP);
+    EXPECT_FALSE(multi_proxy_uri.IsValid());
+    EXPECT_FALSE(multi_proxy_uri.is_direct());
+  }
+}
+
 // Test parsing some invalid inputs.
 TEST(ProxySpecificationUtilTest, InvalidProxyUriToProxyServer) {
   const char* const tests[] = {
@@ -250,5 +274,116 @@
   }
 }
 
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+// A multi-proxy chain that contains any mention of direct will be considered an
+// invalid `ProxyChain()`.
+TEST(ProxySpecificationUtilTest,
+     MultiProxyUrisToProxyChainMultiProxyDirectIsInvalid) {
+  const char* const invalid_multi_proxy_uris[] = {
+      "[direct://xyz]",             // direct with ports
+      "[direct:// direct://]",      // Two directs in chain
+      "[direct:// https://foopy]",  // direct first in chain
+      "[https://foopy direct://]",  // direct later in chain
+  };
+
+  for (const char* uri : invalid_multi_proxy_uris) {
+    ProxyChain multi_proxy_uri =
+        MultiProxyUrisToProxyChain(uri, ProxyServer::SCHEME_HTTPS);
+    EXPECT_FALSE(multi_proxy_uri.IsValid());
+    EXPECT_FALSE(multi_proxy_uri.is_direct());
+  }
+}
+
+// A input containing a single uri of direct will be valid.
+TEST(ProxySpecificationUtilTest,
+     MultiProxyUrisToProxyChainSingleDirectIsValid) {
+  const char* const valid_direct_uris[] = {
+      "direct://",    // non-bracketed direct
+      "[direct://]",  // bracketed direct
+  };
+
+  for (const char* uri : valid_direct_uris) {
+    ProxyChain multi_proxy_uri =
+        MultiProxyUrisToProxyChain(uri, ProxyServer::SCHEME_HTTPS);
+    EXPECT_TRUE(multi_proxy_uri.IsValid());
+    EXPECT_TRUE(multi_proxy_uri.is_direct());
+  }
+}
+
+TEST(ProxySpecificationUtilTest, MultiProxyUrisToProxyChainValid) {
+  const struct {
+    const char* const input_uri;
+    const std::vector<std::string> expected_uris;
+    ProxyServer::Scheme expected_scheme;
+  } tests[] = {
+      // 1 Proxy (w/ and w/o brackets):
+      {"[https://foopy:443]", {"https://foopy:443"}, ProxyServer::SCHEME_HTTPS},
+      {"https://foopy:443", {"https://foopy:443"}, ProxyServer::SCHEME_HTTPS},
+
+      // 2 Proxies:
+      {"[https://foopy:443 https://hoopy:443]",
+       {"https://foopy:443", "https://hoopy:443"},
+       ProxyServer::SCHEME_HTTPS},
+
+      // Extra padding in uris string ignored:
+      {" [https://foopy:443 https://hoopy:443] ",
+       {"https://foopy:443", "https://hoopy:443"},
+       ProxyServer::SCHEME_HTTPS},
+      {"[\thttps://foopy:443 https://hoopy:443\t       ] ",
+       {"https://foopy:443", "https://hoopy:443"},
+       ProxyServer::SCHEME_HTTPS},
+      {"     \t[       https://foopy:443 https://hoopy:443\t        ]",
+       {"https://foopy:443", "https://hoopy:443"},
+       ProxyServer::SCHEME_HTTPS},
+      {"[https://foopy:443  https://hoopy:443]",
+       {"https://foopy:443", "https://hoopy:443"},
+       ProxyServer::SCHEME_HTTPS},  // Delimiter is two spaces.
+      {"[https://foopy \thttps://hoopy]",
+       {"https://foopy:443", "https://hoopy:443"},
+       ProxyServer::SCHEME_HTTPS},  // Delimiter is followed by tab.
+
+      // 3 Proxies:
+      {"[https://foopy:443 https://hoopy:443 https://loopy:443]",
+       {"https://foopy:443", "https://hoopy:443", "https://loopy:443"},
+       ProxyServer::SCHEME_HTTPS},
+  };
+
+  for (const auto& test : tests) {
+    ProxyChain proxy_chain =
+        MultiProxyUrisToProxyChain(test.input_uri, test.expected_scheme);
+
+    EXPECT_TRUE(proxy_chain.IsValid());
+    EXPECT_EQ(proxy_chain.length(), test.expected_uris.size());
+
+    std::vector<ProxyServer> proxies = proxy_chain.proxy_servers();
+    for (size_t i = 0; i < proxies.size(); i++) {
+      const ProxyServer& proxy = proxies[i];
+      EXPECT_TRUE(proxy.is_valid());
+      EXPECT_EQ(test.expected_uris[i], ProxyServerToProxyUri(proxy));
+    }
+  }
+}
+
+// If the input URIs is invalid, an invalid `ProxyChain()` will be returned.
+TEST(ProxySpecificationUtilTest,
+     MultiProxyUrisToProxyChainInvalidFormatReturnsInvalidProxyChain) {
+  const char* const invalid_multi_proxy_uris[] = {
+      "",                                 // Empty string
+      "   ",                              // String with only spaces
+      "[]",                               // No proxies within brackets
+      "https://foopy https://hoopy",      // Missing brackets
+      "[https://foopy https://hoopy",     // Missing bracket
+      "https://foopy https://hoopy]",     // Missing bracket
+      "https://foopy \t   https://hoopy"  // Missing brackets and bad delimiter
+  };
+
+  for (const char* uri : invalid_multi_proxy_uris) {
+    ProxyChain multi_proxy_uri =
+        MultiProxyUrisToProxyChain(uri, ProxyServer::SCHEME_HTTPS);
+    EXPECT_FALSE(multi_proxy_uri.IsValid());
+    EXPECT_FALSE(multi_proxy_uri.is_direct());
+  }
+}
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
 }  // namespace
 }  // namespace net
diff --git a/net/features.gni b/net/features.gni
index 026a38c..a82f99d 100644
--- a/net/features.gni
+++ b/net/features.gni
@@ -54,6 +54,11 @@
 
   # DBSC is only supported on windows for now
   enable_device_bound_sessions = is_win
+
+  # Bracketed URIs parsing is only available for debug builds.
+  # TODO(crbug.com/365771838): Ensure tests are updated if needed if this
+  # feature is changed at all.
+  enable_bracketed_proxy_uris = is_debug
 }
 
 assert(!chrome_root_store_optional || !chrome_root_store_only,
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index c5175cf..56025ed 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -46,6 +46,7 @@
 #include "base/test/test_file_util.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "build/buildflag.h"
 #include "net/base/auth.h"
 #include "net/base/chunked_upload_data_stream.h"
 #include "net/base/completion_once_callback.h"
@@ -99,6 +100,7 @@
 #include "net/log/net_log_source.h"
 #include "net/log/test_net_log.h"
 #include "net/log/test_net_log_util.h"
+#include "net/net_buildflags.h"
 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
 #include "net/proxy_resolution/mock_proxy_resolver.h"
 #include "net/proxy_resolution/proxy_config_service_fixed.h"
@@ -411,6 +413,8 @@
 
 }  // namespace
 
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 class HttpNetworkTransactionTestBase : public PlatformTest,
                                        public WithTaskEnvironment {
  public:
@@ -6784,7 +6788,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -6973,7 +6978,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -7128,7 +7134,8 @@
   // Configure a nested proxy.
   const ProxyServer kProxyServer{ProxyServer::SCHEME_HTTPS,
                                  HostPortPair("proxy.test", 70)};
-  const ProxyChain kNestedProxyChain{{kProxyServer, kProxyServer}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer, kProxyServer}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -7279,7 +7286,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -7445,7 +7453,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -7520,7 +7529,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -7581,7 +7591,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -7681,7 +7692,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -7767,7 +7779,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -8151,7 +8164,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -8387,7 +8401,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -8503,7 +8518,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -8686,7 +8702,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -8756,7 +8773,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -9037,7 +9055,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   const ProxyChain kFirstHopOnlyChain{{kProxyServer1}};
   HttpsNestedProxyNoSocketReuseHelper(kNestedProxyChain, kFirstHopOnlyChain);
@@ -9051,7 +9070,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   const ProxyChain kSecondHopOnlyChain{{kProxyServer2}};
 
@@ -9066,9 +9086,11 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
-  const ProxyChain kReversedChain{{kProxyServer2, kProxyServer1}};
+  const ProxyChain kReversedChain =
+      ProxyChain::ForIpProtection({{kProxyServer2, kProxyServer1}});
 
   HttpsNestedProxyNoSocketReuseHelper(kNestedProxyChain, kReversedChain);
 }
@@ -9092,7 +9114,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
   const ProxyChain kFirstHopOnlyChain{{kProxyServer1}};
   const ProxyChain kSecondHopOnlyChain{{kProxyServer1}};
 
@@ -9356,7 +9379,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   session_deps_.proxy_delegate = std::make_unique<TestProxyDelegate>();
   auto* proxy_delegate =
@@ -9567,7 +9591,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
diff --git a/net/http/http_proxy_connect_job_unittest.cc b/net/http/http_proxy_connect_job_unittest.cc
index 8d3f819..c3f9ac98 100644
--- a/net/http/http_proxy_connect_job_unittest.cc
+++ b/net/http/http_proxy_connect_job_unittest.cc
@@ -85,8 +85,10 @@
 
 const ProxyChain kHttpProxyChain{kHttpProxyServer};
 const ProxyChain kHttpsProxyChain{kHttpsProxyServer};
-const ProxyChain kHttpsNestedProxyChain{
-    {kHttpsProxyServer, kHttpsNestedProxyServer}};
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
+const ProxyChain kHttpsNestedProxyChain =
+    ProxyChain::ForIpProtection({{kHttpsProxyServer, kHttpsNestedProxyServer}});
 
 constexpr char kTestHeaderName[] = "Foo";
 // Note: `kTestSpdyHeaderName` should be a lowercase version of
diff --git a/net/http/http_stream_factory_job_controller_unittest.cc b/net/http/http_stream_factory_job_controller_unittest.cc
index a1ae7a61..ee5c2b8 100644
--- a/net/http/http_stream_factory_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_job_controller_unittest.cc
@@ -646,6 +646,8 @@
   EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
 }
 
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 class JobControllerReconsiderProxyAfterErrorTest
     : public HttpStreamFactoryJobControllerTestBase {
  public:
@@ -1259,8 +1261,10 @@
                                      HostPortPair("badproxyserver", 99)};
   const ProxyServer kBadProxyServer2{
       ProxyServer::SCHEME_HTTPS, HostPortPair("badfallbackproxyserver", 98)};
-  const ProxyChain kNestedProxyChain1{{kBadProxyServer1, kGoodProxyServer}};
-  const ProxyChain kNestedProxyChain2{{kBadProxyServer2, kGoodProxyServer}};
+  const ProxyChain kNestedProxyChain1 =
+      ProxyChain::ForIpProtection({{kBadProxyServer1, kGoodProxyServer}});
+  const ProxyChain kNestedProxyChain2 =
+      ProxyChain::ForIpProtection({{kBadProxyServer2, kGoodProxyServer}});
 
   for (GURL dest_url :
        {GURL("http://www.example.com"), GURL("https://www.example.com")}) {
@@ -1497,8 +1501,10 @@
                                      HostPortPair("badproxyserver", 99)};
   const ProxyServer kBadProxyServer2{
       ProxyServer::SCHEME_HTTPS, HostPortPair("badfallbackproxyserver", 98)};
-  const ProxyChain kNestedProxyChain1{{kGoodProxyServer, kBadProxyServer1}};
-  const ProxyChain kNestedProxyChain2{{kGoodProxyServer, kBadProxyServer2}};
+  const ProxyChain kNestedProxyChain1 =
+      ProxyChain::ForIpProtection({{kGoodProxyServer, kBadProxyServer1}});
+  const ProxyChain kNestedProxyChain2 =
+      ProxyChain::ForIpProtection({{kGoodProxyServer, kBadProxyServer2}});
 
   for (GURL dest_url :
        {GURL("http://www.example.com"), GURL("https://www.example.com")}) {
diff --git a/net/proxy_resolution/proxy_config.cc b/net/proxy_resolution/proxy_config.cc
index 7b0475b6..3ebb269 100644
--- a/net/proxy_resolution/proxy_config.cc
+++ b/net/proxy_resolution/proxy_config.cc
@@ -7,13 +7,16 @@
 #include <memory>
 #include <utility>
 
+#include "base/check.h"
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
+#include "build/buildflag.h"
 #include "net/base/proxy_server.h"
 #include "net/base/proxy_string_util.h"
+#include "net/net_buildflags.h"
 #include "net/proxy_resolution/proxy_info.h"
 
 namespace net {
@@ -24,18 +27,22 @@
 void AddProxyListToValue(const char* name,
                          const ProxyList& proxies,
                          base::Value::Dict* dict) {
-  if (!proxies.IsEmpty())
+  if (!proxies.IsEmpty()) {
     dict->Set(name, proxies.ToValue());
+  }
 }
 
 // Split the |uri_list| on commas and add each entry to |proxy_list| in turn.
 void AddProxyURIListToProxyList(std::string uri_list,
                                 ProxyList* proxy_list,
-                                ProxyServer::Scheme default_scheme) {
+                                ProxyServer::Scheme default_scheme,
+                                bool allow_bracketed_proxy_chains) {
   base::StringTokenizer proxy_uri_list(uri_list, ",");
   while (proxy_uri_list.GetNext()) {
     proxy_list->AddProxyChain(
-        ProxyUriToProxyChain(proxy_uri_list.token(), default_scheme));
+        allow_bracketed_proxy_chains
+            ? MultiProxyUrisToProxyChain(proxy_uri_list.token(), default_scheme)
+            : ProxyUriToProxyChain(proxy_uri_list.token(), default_scheme));
   }
 }
 
@@ -82,7 +89,9 @@
   }
 }
 
-void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
+void ProxyConfig::ProxyRules::ParseFromString(
+    const std::string& proxy_rules,
+    bool allow_bracketed_proxy_chains) {
   // Reset.
   type = Type::EMPTY;
   single_proxies = ProxyList();
@@ -91,6 +100,11 @@
   proxies_for_ftp = ProxyList();
   fallback_proxies = ProxyList();
 
+#if !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+  // `allow_multi_proxy_chains` can only be true in non-release builds;
+  CHECK(!allow_bracketed_proxy_chains);
+#endif  // !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+
   base::StringTokenizer proxy_server_list(proxy_rules, ";");
   while (proxy_server_list.GetNext()) {
     base::StringTokenizer proxy_server_for_scheme(
@@ -103,11 +117,12 @@
       // this is a regular proxy server configuration, i.e. proxies
       // are not configured per protocol.
       if (!proxy_server_for_scheme.GetNext()) {
-        if (type == Type::PROXY_LIST_PER_SCHEME)
+        if (type == Type::PROXY_LIST_PER_SCHEME) {
           continue;  // Unexpected.
-        AddProxyURIListToProxyList(url_scheme,
-                                   &single_proxies,
-                                   ProxyServer::SCHEME_HTTP);
+        }
+        AddProxyURIListToProxyList(url_scheme, &single_proxies,
+                                   ProxyServer::SCHEME_HTTP,
+                                   allow_bracketed_proxy_chains);
         type = Type::PROXY_LIST;
         return;
       }
@@ -132,9 +147,9 @@
       }
 
       if (entry) {
-        AddProxyURIListToProxyList(proxy_server_for_scheme.token(),
-                                   entry,
-                                   default_scheme);
+        AddProxyURIListToProxyList(proxy_server_for_scheme.token(), entry,
+                                   default_scheme,
+                                   allow_bracketed_proxy_chains);
       }
     }
   }
@@ -142,14 +157,18 @@
 
 const ProxyList* ProxyConfig::ProxyRules::MapUrlSchemeToProxyList(
     const std::string& url_scheme) const {
-  const ProxyList* proxy_server_list = const_cast<ProxyRules*>(this)->
-      MapUrlSchemeToProxyListNoFallback(url_scheme);
-  if (proxy_server_list && !proxy_server_list->IsEmpty())
+  const ProxyList* proxy_server_list =
+      const_cast<ProxyRules*>(this)->MapUrlSchemeToProxyListNoFallback(
+          url_scheme);
+  if (proxy_server_list && !proxy_server_list->IsEmpty()) {
     return proxy_server_list;
-  if (url_scheme == "ws" || url_scheme == "wss")
+  }
+  if (url_scheme == "ws" || url_scheme == "wss") {
     return GetProxyListForWebSocketScheme();
-  if (!fallback_proxies.IsEmpty())
+  }
+  if (!fallback_proxies.IsEmpty()) {
     return &fallback_proxies;
+  }
   return nullptr;  // No mapping for this scheme. Use direct.
 }
 
@@ -166,12 +185,15 @@
 ProxyList* ProxyConfig::ProxyRules::MapUrlSchemeToProxyListNoFallback(
     const std::string& scheme) {
   DCHECK_EQ(Type::PROXY_LIST_PER_SCHEME, type);
-  if (scheme == "http")
+  if (scheme == "http") {
     return &proxies_for_http;
-  if (scheme == "https")
+  }
+  if (scheme == "https") {
     return &proxies_for_https;
-  if (scheme == "ftp")
+  }
+  if (scheme == "ftp") {
     return &proxies_for_ftp;
+  }
   return nullptr;  // No mapping for this scheme.
 }
 
@@ -198,12 +220,15 @@
   // including non-SOCKS. In this case "fallback_proxies" is
   // still prioritized over proxies_for_http and
   // proxies_for_https.
-  if (!fallback_proxies.IsEmpty())
+  if (!fallback_proxies.IsEmpty()) {
     return &fallback_proxies;
-  if (!proxies_for_https.IsEmpty())
+  }
+  if (!proxies_for_https.IsEmpty()) {
     return &proxies_for_https;
-  if (!proxies_for_http.IsEmpty())
+  }
+  if (!proxies_for_http.IsEmpty()) {
     return &proxies_for_http;
+  }
   return nullptr;
 }
 
@@ -211,10 +236,14 @@
 
 ProxyConfig::ProxyConfig(const ProxyConfig& config) = default;
 
-ProxyConfig::~ProxyConfig() = default;
+ProxyConfig::ProxyConfig(ProxyConfig&& config) = default;
 
 ProxyConfig& ProxyConfig::operator=(const ProxyConfig& config) = default;
 
+ProxyConfig& ProxyConfig::operator=(ProxyConfig&& config) = default;
+
+ProxyConfig::~ProxyConfig() = default;
+
 bool ProxyConfig::Equals(const ProxyConfig& other) const {
   return auto_detect_ == other.auto_detect_ && pac_url_ == other.pac_url_ &&
          pac_mandatory_ == other.pac_mandatory_ &&
@@ -235,12 +264,14 @@
   base::Value::Dict dict;
 
   // Output the automatic settings.
-  if (auto_detect_)
+  if (auto_detect_) {
     dict.Set("auto_detect", auto_detect_);
+  }
   if (has_pac_url()) {
     dict.Set("pac_url", pac_url_.possibly_invalid_spec());
-    if (pac_mandatory_)
+    if (pac_mandatory_) {
       dict.Set("pac_mandatory", pac_mandatory_);
+    }
   }
   if (from_system_) {
     dict.Set("from_system", from_system_);
@@ -268,13 +299,15 @@
     // Output the bypass rules.
     const ProxyBypassRules& bypass = proxy_rules_.bypass_rules;
     if (!bypass.rules().empty()) {
-      if (proxy_rules_.reverse_bypass)
+      if (proxy_rules_.reverse_bypass) {
         dict.Set("reverse_bypass", true);
+      }
 
       base::Value::List list;
 
-      for (const auto& bypass_rule : bypass.rules())
+      for (const auto& bypass_rule : bypass.rules()) {
         list.Append(bypass_rule->ToString());
+      }
 
       dict.Set("bypass_list", std::move(list));
     }
diff --git a/net/proxy_resolution/proxy_config.h b/net/proxy_resolution/proxy_config.h
index b91c8aea..bd60de3 100644
--- a/net/proxy_resolution/proxy_config.h
+++ b/net/proxy_resolution/proxy_config.h
@@ -36,6 +36,8 @@
  public:
   // ProxyRules describes the "manual" proxy settings.
   struct NET_EXPORT ProxyRules {
+    // A `Type` other than `EMPTY` does not guarantee the presence of a valid
+    // proxy chain.
     enum class Type {
       EMPTY,
       PROXY_LIST,
@@ -81,6 +83,8 @@
     //    to all otherwise unspecified url-schemes, however the default proxy-
     //    scheme for proxy urls in the 'socks' list is understood to be
     //    socks4:// if unspecified.
+    //  * In debug mode, allow_bracketed_proxy_chains can be set to true. In
+    //    this case, brackets can be used to format multi-proxy chains.
     //
     // For example:
     //   "http=foopy:80;ftp=foopy2"  -- use HTTP proxy "foopy:80" for http://
@@ -102,7 +106,8 @@
     //   "http=foopy;socks=foopy2   --  use HTTP proxy "foopy" for http URLs,
     //                                  and use socks4://foopy2 for all other
     //                                  URLs.
-    void ParseFromString(const std::string& proxy_rules);
+    void ParseFromString(const std::string& proxy_rules,
+                         bool allow_bracketed_proxy_chains = false);
 
     // Returns one of {&proxies_for_http, &proxies_for_https, &proxies_for_ftp,
     // &fallback_proxies}, or NULL if there is no proxy to use.
@@ -155,8 +160,10 @@
 
   ProxyConfig();
   ProxyConfig(const ProxyConfig& config);
-  ~ProxyConfig();
+  ProxyConfig(ProxyConfig&& config);
   ProxyConfig& operator=(const ProxyConfig& config);
+  ProxyConfig& operator=(ProxyConfig&& config);
+  ~ProxyConfig();
 
   // Returns true if the given config is equivalent to this config.
   bool Equals(const ProxyConfig& other) const;
diff --git a/net/proxy_resolution/proxy_config_unittest.cc b/net/proxy_resolution/proxy_config_unittest.cc
index c1d6999..7e8551d 100644
--- a/net/proxy_resolution/proxy_config_unittest.cc
+++ b/net/proxy_resolution/proxy_config_unittest.cc
@@ -5,8 +5,11 @@
 #include "net/proxy_resolution/proxy_config.h"
 
 #include "base/json/json_writer.h"
+#include "base/test/gtest_util.h"
 #include "base/values.h"
+#include "build/buildflag.h"
 #include "net/base/proxy_string_util.h"
+#include "net/net_buildflags.h"
 #include "net/proxy_resolution/proxy_config_service_common_unittest.h"
 #include "net/proxy_resolution/proxy_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -99,6 +102,38 @@
   EXPECT_TRUE(config2.Equals(config1));
 }
 
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+TEST(ProxyConfigTest, EqualsMultiProxyChains) {
+  ProxyConfig config1;
+  ProxyConfig config2;
+
+  config2.proxy_rules().type =
+      ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
+  config2.proxy_rules().proxies_for_https.SetSingleProxyChain(
+      MultiProxyUrisToProxyChain("[https://foopy:443 https://hoopy:443]",
+                                 ProxyServer::SCHEME_HTTPS));
+
+  EXPECT_FALSE(config1.Equals(config2));
+  EXPECT_FALSE(config2.Equals(config1));
+
+  config1.proxy_rules().type =
+      ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME;
+  config1.proxy_rules().proxies_for_https.SetSingleProxyChain(
+      MultiProxyUrisToProxyChain("[https://foopy:80 https://hoopy:80]",
+                                 ProxyServer::SCHEME_HTTPS));
+
+  EXPECT_FALSE(config1.Equals(config2));
+  EXPECT_FALSE(config2.Equals(config1));
+
+  config1.proxy_rules().proxies_for_https.SetSingleProxyChain(
+      MultiProxyUrisToProxyChain("[https://foopy https://hoopy]",
+                                 ProxyServer::SCHEME_HTTPS));
+
+  EXPECT_TRUE(config1.Equals(config2));
+  EXPECT_TRUE(config2.Equals(config1));
+}
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+
 struct ProxyConfigToValueTestCase {
   ProxyConfig config;
   const char* expected_value_json;
@@ -208,6 +243,25 @@
       "\"]}"};
 }
 
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+// Multi-proxy chains present
+ProxyConfigToValueTestCase GetTestCaseMultiProxyChainProxyPerScheme() {
+  ProxyConfig config;
+  config.proxy_rules().ParseFromString(
+      "http=[https://proxy1:8080 https://proxy2:8080];https=socks5://proxy2",
+      /*allow_bracketed_proxy_chains=*/true);
+  config.proxy_rules().bypass_rules.AddRuleFromString("*.google.com");
+  config.set_pac_url(GURL("http://wpad/wpad.dat"));
+  config.set_auto_detect(true);
+
+  return {std::move(config),
+          "{\"auto_detect\":true,\"bypass_list\":[\"*.google.com\"],\"pac_"
+          "url\":\"http://wpad/"
+          "wpad.dat\",\"proxy_per_scheme\":{\"http\":[\"[https://proxy1:8080, "
+          "https://proxy2:8080]\"],\"https\":[\"[socks5://proxy2:1080]\"]}}"};
+}
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+
 INSTANTIATE_TEST_SUITE_P(
     All,
     ProxyConfigToValueTest,
@@ -220,6 +274,9 @@
                     GetTestCaseSingleProxyWithBypass(),
                     GetTestCaseSingleProxyWithReversedBypass(),
                     GetTestCaseProxyPerScheme(),
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+                    GetTestCaseMultiProxyChainProxyPerScheme(),
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
                     GetTestCaseSingleProxyList()));
 
 TEST(ProxyConfigTest, ParseProxyRules) {
@@ -284,7 +341,7 @@
       },
 
       // Give a scheme-specific proxy as well as a non-scheme specific.
-      // The first entry "foopy" takes precedance marking this list as
+      // The first entry "foopy" takes precedence marking this list as
       // Type::PROXY_LIST.
       {
           "foopy ; ftp=ftp-proxy",
@@ -298,7 +355,7 @@
       },
 
       // Give a scheme-specific proxy as well as a non-scheme specific.
-      // The first entry "ftp=ftp-proxy" takes precedance marking this list as
+      // The first entry "ftp=ftp-proxy" takes precedence marking this list as
       // Type::PROXY_LIST_PER_SCHEME.
       {
           "ftp=ftp-proxy ; foopy",
@@ -433,13 +490,23 @@
           nullptr,
       },
 
+      // Multi-proxy bracketed URIs will result in no proxy being set
+      {
+          "http=[https://proxy1:8080 https://proxy2:8080]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
+          nullptr,
+          nullptr,
+          nullptr,
+          nullptr,
+          nullptr,
+      }
+
   };
 
   ProxyConfig config;
 
   for (const auto& test : tests) {
     config.proxy_rules().ParseFromString(test.proxy_rules);
-
     EXPECT_EQ(test.type, config.proxy_rules().type);
     ExpectProxyServerEquals(test.single_proxy,
                             config.proxy_rules().single_proxies);
@@ -454,10 +521,181 @@
   }
 }
 
+#if !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+// In release builds, `ParseFromString` should not allow parsing of multi-proxy
+// chains by setting bool to true. A true value should crash.
+TEST(ProxyConfigTest, ParseProxyRulesDisallowMultiProxyChainsInReleaseBuilds) {
+  ProxyConfig config;
+
+  EXPECT_CHECK_DEATH(config.proxy_rules().ParseFromString(
+      "http=[https://proxy1:8080 https://proxy2:8080]", true));
+}
+#endif  // !BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+// Tests for multi-proxy chains which are currently only allowed in debug mode.
+TEST(ProxyConfigTest, MultiProxyChainsParseProxyRules) {
+  const struct {
+    const char* proxy_rules;
+
+    ProxyConfig::ProxyRules::Type type;
+    // For multi-proxy chains, proxies within a single chain will be formatted
+    // within a bracket separated by a space and comma.
+    const char* single_proxy;
+    const char* proxy_for_http;
+    const char* proxy_for_https;
+    const char* proxy_for_ftp;
+    const char* fallback_proxy;
+  } tests[] = {
+
+      // One HTTP proxy for all schemes.
+      {
+          "[https://proxy1:8080]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST,
+          "HTTPS proxy1:8080",
+          nullptr,
+          nullptr,
+          nullptr,
+          nullptr,
+      },
+
+      // Multiple proxies for all schemes.
+      {
+          "[https://proxy1:8080 https://proxy2:8080],[https://proxy3:8080]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST,
+          "[https://proxy1:8080, https://proxy2:8080];HTTPS proxy3:8080",
+          nullptr,
+          nullptr,
+          nullptr,
+          nullptr,
+      },
+
+      // Only specify a proxy chain for "http://" urls.
+      {
+          "http=[https://proxy1:8080 https://proxy2:8080]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
+          nullptr,
+          "[https://proxy1:8080, https://proxy2:8080]",
+          nullptr,
+          nullptr,
+          nullptr,
+      },
+
+      // Specify different multi-proxy chains for different schemes.
+      {
+          "http=[https://proxy1:8080 https://proxy2:8080] ; "
+          "https=[https://proxy3:8080 https://proxy4:8080]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
+          nullptr,
+          "[https://proxy1:8080, https://proxy2:8080]",
+          "[https://proxy3:8080, https://proxy4:8080]",
+          nullptr,
+          nullptr,
+      },
+
+      // Give a scheme-specific proxy as well as a non-scheme specific.
+      // The first entry takes precedence marking this list as Type::PROXY_LIST.
+      {
+          "[https://proxy1:8080 https://proxy2:8080] ; "
+          "http=[https://proxy3:8080 https://proxy4:8080]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST,
+          "[https://proxy1:8080, https://proxy2:8080]",
+          nullptr,
+          nullptr,
+          nullptr,
+          nullptr,
+      },
+
+      // Give a scheme-specific proxy as well as a non-scheme specific.
+      // The first entry takes precedence marking this list as
+      // Type::PROXY_LIST_PER_SCHEME.
+      {
+          "http=[https://proxy3:8080 https://proxy4:8080] ; "
+          "[https://proxy1:8080 https://proxy2:8080]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
+          nullptr,
+          "[https://proxy3:8080, https://proxy4:8080]",
+          nullptr,
+          nullptr,
+          nullptr,
+      },
+
+      // Include a list of entries for a single scheme.
+      {
+          "ftp=[https://proxy1:80 https://proxy2:80],[https://proxy3:80 "
+          "https://proxy4:80],[https://proxy5:80 https://proxy6:80]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
+          nullptr,
+          nullptr,
+          nullptr,
+          "[https://proxy1:80, https://proxy2:80];[https://proxy3:80, "
+          "https://proxy4:80];[https://proxy5:80, https://proxy6:80]",
+          nullptr,
+      },
+
+      // Include multiple entries for the same scheme -- they accumulate.
+      {
+          "http=[https://proxy1:80 https://proxy2:80]; http=[https://proxy3:80 "
+          "https://proxy4:80],[https://proxy5:80 https://proxy6:80]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
+          nullptr,
+          "[https://proxy1:80, https://proxy2:80];[https://proxy3:80, "
+          "https://proxy4:80];[https://proxy5:80, https://proxy6:80]",
+          nullptr,
+          nullptr,
+          nullptr,
+      },
+
+      // Include lists of entries for multiple schemes.
+      {
+          "http=[https://proxy1:80 https://proxy2:80]; ftp=[https://proxy3:80 "
+          "https://proxy4:80],[https://proxy5:80 https://proxy6:80]",
+          ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
+          nullptr,
+          "[https://proxy1:80, https://proxy2:80]",
+          nullptr,
+          "[https://proxy3:80, https://proxy4:80];[https://proxy5:80, "
+          "https://proxy6:80]",
+          nullptr,
+      },
+
+      // Include unsupported schemes -- they are discarded.
+      {
+          "crazy=[https://proxy1:80 https://proxy2:80] ; foo=bar ; "
+          "https=[https://proxy3:80 https://proxy4:80]",
+
+          ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME,
+          nullptr,
+          nullptr,
+          "[https://proxy3:80, https://proxy4:80]",
+          nullptr,
+          nullptr,
+      },
+  };
+
+  ProxyConfig config;
+
+  for (const auto& test : tests) {
+    config.proxy_rules().ParseFromString(test.proxy_rules, true);
+    EXPECT_EQ(test.type, config.proxy_rules().type);
+    ExpectProxyServerEquals(test.single_proxy,
+                            config.proxy_rules().single_proxies);
+    ExpectProxyServerEquals(test.proxy_for_http,
+                            config.proxy_rules().proxies_for_http);
+    ExpectProxyServerEquals(test.proxy_for_https,
+                            config.proxy_rules().proxies_for_https);
+    ExpectProxyServerEquals(test.proxy_for_ftp,
+                            config.proxy_rules().proxies_for_ftp);
+    ExpectProxyServerEquals(test.fallback_proxy,
+                            config.proxy_rules().fallback_proxies);
+  }
+}
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+
 TEST(ProxyConfigTest, ProxyRulesSetBypassFlag) {
   // Test whether the did_bypass_proxy() flag is set in proxy info correctly.
   ProxyConfig::ProxyRules rules;
-  ProxyInfo  result;
+  ProxyInfo result;
 
   rules.ParseFromString("http=httpproxy:80");
   rules.bypass_rules.AddRuleFromString(".com");
diff --git a/net/proxy_resolution/proxy_list_unittest.cc b/net/proxy_resolution/proxy_list_unittest.cc
index 93ff1e74..97ba8725 100644
--- a/net/proxy_resolution/proxy_list_unittest.cc
+++ b/net/proxy_resolution/proxy_list_unittest.cc
@@ -6,10 +6,13 @@
 
 #include <vector>
 
+#include "base/logging.h"
+#include "build/buildflag.h"
 #include "net/base/net_errors.h"
 #include "net/base/proxy_server.h"
 #include "net/base/proxy_string_util.h"
 #include "net/log/net_log_with_source.h"
+#include "net/net_buildflags.h"
 #include "net/proxy_resolution/proxy_retry_info.h"
 #include "net/test/gtest_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -27,42 +30,51 @@
     const char* pac_input;
     const char* debug_output;
   } tests[] = {
-    // Valid inputs:
-    {  "PROXY foopy:10",
-       "PROXY foopy:10",
-    },
-    {  " DIRECT",  // leading space.
-       "DIRECT",
-    },
-    {  "PROXY foopy1 ; proxy foopy2;\t DIRECT",
-       "PROXY foopy1:80;PROXY foopy2:80;DIRECT",
-    },
-    {  "proxy foopy1 ; SOCKS foopy2",
-       "PROXY foopy1:80;SOCKS foopy2:1080",
-    },
-    // Try putting DIRECT first.
-    {  "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
-       "DIRECT;PROXY foopy1:80;DIRECT;SOCKS5 foopy2:1080;DIRECT",
-    },
-    // Try putting DIRECT consecutively.
-    {  "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
-       "DIRECT;PROXY foopy1:80;DIRECT;DIRECT",
-    },
+      // Valid inputs:
+      {
+          "PROXY foopy:10",
+          "PROXY foopy:10",
+      },
+      {
+          " DIRECT",  // leading space.
+          "DIRECT",
+      },
+      {
+          "PROXY foopy1 ; proxy foopy2;\t DIRECT",
+          "PROXY foopy1:80;PROXY foopy2:80;DIRECT",
+      },
+      {
+          "proxy foopy1 ; SOCKS foopy2",
+          "PROXY foopy1:80;SOCKS foopy2:1080",
+      },
+      // Try putting DIRECT first.
+      {
+          "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
+          "DIRECT;PROXY foopy1:80;DIRECT;SOCKS5 foopy2:1080;DIRECT",
+      },
+      // Try putting DIRECT consecutively.
+      {
+          "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
+          "DIRECT;PROXY foopy1:80;DIRECT;DIRECT",
+      },
 
-    // Invalid inputs (parts which aren't understood get
-    // silently discarded):
-    //
-    // If the proxy list string parsed to empty, automatically fall-back to
-    // DIRECT.
-    {  "PROXY-foopy:10",
-       "DIRECT",
-    },
-    {  "PROXY",
-       "DIRECT",
-    },
-    {  "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
-       "PROXY foopy1:80;SOCKS5 foopy2:1080",
-    },
+      // Invalid inputs (parts which aren't understood get
+      // silently discarded):
+      //
+      // If the proxy list string parsed to empty, automatically fall-back to
+      // DIRECT.
+      {
+          "PROXY-foopy:10",
+          "DIRECT",
+      },
+      {
+          "PROXY",
+          "DIRECT",
+      },
+      {
+          "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
+          "PROXY foopy1:80;SOCKS5 foopy2:1080",
+      },
   };
 
   for (const auto& test : tests) {
@@ -103,14 +115,14 @@
 }
 
 TEST(ProxyListTest, RemoveProxiesWithoutSchemeWithProxyChains) {
-  const ProxyChain kProxyChainFooHttps({
+  const auto kProxyChainFooHttps = ProxyChain::ForIpProtection({
       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
                                          "foo-a", 443),
       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
                                          "foo-b", 443),
   });
-  const ProxyChain kProxyChainBarMixed({
-      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_SOCKS5,
+  const auto kProxyChainBarMixed = ProxyChain::ForIpProtection({
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_QUIC,
                                          "bar-a", 443),
       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
                                          "bar-b", 443),
@@ -379,7 +391,100 @@
   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
   EXPECT_EQ(list.ToPacString(), "HTTPS foo:443");
+  // ToPacString should fail for proxy chains.
+  list.AddProxyChain(ProxyChain::ForIpProtection({
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "foo-a", 443),
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "foo-b", 443),
+  }));
+  EXPECT_DEATH_IF_SUPPORTED(list.ToPacString(), "");
+}
 
+TEST(ProxyListTest, ToDebugString) {
+  ProxyList list;
+  list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
+      ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
+  list.AddProxyChain(ProxyChain::ForIpProtection({
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "foo-a", 443),
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "foo-b", 443),
+  }));
+
+  EXPECT_EQ(
+      list.ToDebugString(),
+      "HTTPS foo:443;[https://foo-a:443, https://foo-b:443] (IP Protection)");
+}
+
+TEST(ProxyListTest, ToValue) {
+  ProxyList list;
+  list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
+      ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
+  list.AddProxyChain(ProxyChain::ForIpProtection({
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "foo-a", 443),
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "foo-b", 443),
+  }));
+
+  base::Value expected(base::Value::Type::LIST);
+  base::Value::List& exp_list = expected.GetList();
+  exp_list.Append("[https://foo:443]");
+  exp_list.Append("[https://foo-a:443, https://foo-b:443] (IP Protection)");
+
+  EXPECT_EQ(list.ToValue(), expected);
+}
+
+#if BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
+// The following tests are for non-release builds where multi-proxy chains are
+// permitted outside of Ip Protection.
+
+TEST(ProxyListTest,
+     NonIpProtectionMultiProxyChainRemoveProxiesWithoutSchemeWithProxyChains) {
+  const ProxyChain kProxyChainFooHttps({
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "foo-a", 443),
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "foo-b", 443),
+  });
+  const ProxyChain kProxyChainBarMixed({
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_QUIC,
+                                         "bar-a", 443),
+      ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
+                                         "bar-b", 443),
+  });
+  const ProxyChain kProxyChainGraultSocks = ProxyChain::FromSchemeHostAndPort(
+      ProxyServer::Scheme::SCHEME_SOCKS4, "grault", 443);
+
+  ProxyList list;
+  list.AddProxyChain(kProxyChainFooHttps);
+  list.AddProxyChain(kProxyChainBarMixed);
+  list.AddProxyChain(kProxyChainGraultSocks);
+  list.AddProxyChain(ProxyChain::Direct());
+
+  // Remove anything that isn't entirely HTTPS.
+  list.RemoveProxiesWithoutScheme(ProxyServer::SCHEME_HTTPS);
+
+  std::vector<net::ProxyChain> expected = {
+      kProxyChainFooHttps,
+      ProxyChain::Direct(),
+  };
+  EXPECT_EQ(list.AllChains(), expected);
+}
+
+// `ToPacString` should only be called if the list contains no multi-proxy
+// chains, as those cannot be represented in PAC syntax. This is not an issue in
+// release builds because a `ProxyChain` constructed with multiple proxy servers
+// would automatically default to an empty, invalid
+// `ProxyChain` (unless for Ip Protection); however, in non-release builds,
+// multi-proxy chains are permitted which means they must be CHECKED when this
+// function is called.
+TEST(ProxyListTest, NonIpProtectionMultiProxyChainToPacString) {
+  ProxyList list;
+  list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
+      ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
+  EXPECT_EQ(list.ToPacString(), "HTTPS foo:443");
   // ToPacString should fail for proxy chains.
   list.AddProxyChain(ProxyChain({
       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
@@ -390,7 +495,7 @@
   EXPECT_DEATH_IF_SUPPORTED(list.ToPacString(), "");
 }
 
-TEST(ProxyListTest, ToDebugString) {
+TEST(ProxyListTest, NonIpProtectionMultiProxyChainToDebugString) {
   ProxyList list;
   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
@@ -405,7 +510,7 @@
             "HTTPS foo:443;[https://foo-a:443, https://foo-b:443]");
 }
 
-TEST(ProxyListTest, ToValue) {
+TEST(ProxyListTest, NonIpProtectionMultiProxyChainToValue) {
   ProxyList list;
   list.AddProxyChain(ProxyChain::FromSchemeHostAndPort(
       ProxyServer::Scheme::SCHEME_HTTPS, "foo", 443));
@@ -423,6 +528,7 @@
 
   EXPECT_EQ(list.ToValue(), expected);
 }
+#endif  // BUILDFLAG(ENABLE_BRACKETED_PROXY_URIS)
 
 }  // anonymous namespace
 
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 08b3927..9dbc98b1 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -726,7 +726,7 @@
       /*alpn_protos=*/nullptr,
       /*application_settings=*/nullptr,
       /*ignore_certificate_errors=*/nullptr,
-      /*early_data_enabled=*/nullptr};
+      /*enable_early_data=*/nullptr};
   bool connect_backup_jobs_enabled_;
   MockClientSocketFactory client_socket_factory_;
   RecordingNetLogObserver net_log_observer_;
@@ -5951,11 +5951,13 @@
   EXPECT_EQ(1u, pool_->IdleSocketCountInGroup(kGroupId));
 }
 
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 TEST_F(ClientSocketPoolBaseTest, RefreshProxyRefreshesAllGroups) {
   // Create a proxy chain containing `myproxy` (which is refreshed) and
   // nonrefreshedproxy (which is not), verifying that if any proxy in a chain is
   // refreshed, all groups are refreshed.
-  ProxyChain proxy_chain({
+  auto proxy_chain = ProxyChain::ForIpProtection({
       PacResultElementToProxyServer("HTTPS myproxy:70"),
       PacResultElementToProxyServer("HTTPS nonrefreshedproxy:70"),
   });
diff --git a/net/socket/connect_job_factory_unittest.cc b/net/socket/connect_job_factory_unittest.cc
index 69bd2b0e..9079972e 100644
--- a/net/socket/connect_job_factory_unittest.cc
+++ b/net/socket/connect_job_factory_unittest.cc
@@ -139,6 +139,8 @@
   std::vector<scoped_refptr<TransportSocketParams>> params_;
 };
 
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 class ConnectJobFactoryTest : public TestWithTaskEnvironment {
  public:
   ConnectJobFactoryTest() {
@@ -575,7 +577,8 @@
                                   HostPortPair("proxy1.test", 443)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 443)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   std::unique_ptr<ConnectJob> job = factory_->CreateConnectJob(
       kEndpoint, kNestedProxyChain, TRAFFIC_ANNOTATION_FOR_TESTS,
@@ -652,7 +655,8 @@
                                   HostPortPair("proxy1.test", 443)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 443)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   std::unique_ptr<ConnectJob> job = factory_->CreateConnectJob(
       /*using_ssl=*/false, kEndpoint, kNestedProxyChain,
@@ -729,7 +733,8 @@
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 443)};
 
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   std::unique_ptr<ConnectJob> job = factory_->CreateConnectJob(
       kEndpoint, kNestedProxyChain, TRAFFIC_ANNOTATION_FOR_TESTS,
@@ -825,7 +830,8 @@
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 443)};
 
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   std::unique_ptr<ConnectJob> job = factory_->CreateConnectJob(
       /*using_ssl=*/true, kEndpoint, kNestedProxyChain,
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index f79f813..a021e4b 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -187,8 +187,9 @@
 
     // Start the transaction, read some data, finish.
     void RunDefaultTest() {
-      if (!StartDefaultTest())
+      if (!StartDefaultTest()) {
         return;
+      }
       FinishDefaultTest();
     }
 
@@ -228,8 +229,9 @@
       output_.rv = callback_.WaitForResult();
       // Finish async network reads/writes.
       base::RunLoop().RunUntilIdle();
-      if (output_.rv != OK)
+      if (output_.rv != OK) {
         session_->spdy_session_pool()->CloseCurrentSessions(ERR_ABORTED);
+      }
     }
 
     void WaitForCallbackToComplete() { output_.rv = callback_.WaitForResult(); }
@@ -281,8 +283,9 @@
         SocketDataProvider* data,
         std::unique_ptr<SSLSocketDataProvider> ssl_provider) {
       data_vector_.push_back(data);
-      if (ssl_provider->next_proto == kProtoUnknown)
+      if (ssl_provider->next_proto == kProtoUnknown) {
         ssl_provider->next_proto = kProtoHTTP2;
+      }
       // Even when next_protos only includes HTTP1, `application_settions`
       // always includes the full list from the HttpNetworkSession. The
       // SSLClientSocket layer, which is mocked out in these tests, is the layer
@@ -943,7 +946,8 @@
   spdy::SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true));
 
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(req2, 3),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(req2, 3),
       CreateMockWrite(req3, 6),
   };
   MockRead reads[] = {
@@ -1022,7 +1026,8 @@
   spdy::SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
 
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(req2, 3),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(req2, 3),
   };
   MockRead reads[] = {
       CreateMockRead(resp, 1),  CreateMockRead(body, 2),
@@ -1099,7 +1104,8 @@
   spdy::SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
 
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(req2, 3),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(req2, 3),
   };
   MockRead reads[] = {
       CreateMockRead(resp, 1),  CreateMockRead(body, 2),
@@ -1207,8 +1213,10 @@
   spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
 
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5),
-      CreateMockWrite(req2, 6), CreateMockWrite(req3, 10),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(settings_ack, 5),
+      CreateMockWrite(req2, 6),
+      CreateMockWrite(req3, 10),
   };
 
   MockRead reads[] = {
@@ -1335,7 +1343,8 @@
       spdy_util_.ConstructSpdySettings(settings));
   spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(settings_ack, 5),
       // By making these synchronous, it guarantees that they are not *started*
       // before their sequence number, which in turn verifies that only a single
       // request is in-flight at a time.
@@ -1467,7 +1476,8 @@
   spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck());
 
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 5),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(settings_ack, 5),
       CreateMockWrite(req2, 6),
   };
   MockRead reads[] = {
@@ -1548,8 +1558,9 @@
 
  private:
   void OnComplete(int result) {
-    if (result < 0)
+    if (result < 0) {
       transaction_.reset();
+    }
 
     SetResult(result);
   }
@@ -1827,7 +1838,8 @@
       spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
   spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(body, 1),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(body, 1),
   };
 
   spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
@@ -1862,8 +1874,10 @@
   spdy::SpdySerializedFrame chunk2(spdy_util_.ConstructSpdyDataFrame(1, false));
   spdy::SpdySerializedFrame chunk3(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(chunk1, 1),
-      CreateMockWrite(chunk2, 2), CreateMockWrite(chunk3, 3),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(chunk1, 1),
+      CreateMockWrite(chunk2, 2),
+      CreateMockWrite(chunk3, 3),
   };
 
   spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
@@ -1984,7 +1998,8 @@
       spdy_util_.ConstructChunkedSpdyPost(nullptr, 0));
   spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(body, 3),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(body, 3),
   };
   spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   MockRead reads[] = {
@@ -2072,7 +2087,8 @@
   spdy::SpdySerializedFrame rst(
       spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(rst, 2),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(rst, 2),
   };
   SequencedSocketData data(reads, writes);
   NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr);
@@ -2089,7 +2105,8 @@
   spdy::SpdySerializedFrame rst(
       spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(rst, 4),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(rst, 4),
   };
 
   spdy::SpdySerializedFrame resp0(
@@ -2134,12 +2151,11 @@
   spdy::SpdySerializedFrame rst(
       spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_PROTOCOL_ERROR));
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(rst, 2),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(rst, 2),
   };
 
-  const char* const headers[] = {
-    "transfer-encoding", "chunked"
-  };
+  const char* const headers[] = {"transfer-encoding", "chunked"};
   spdy::SpdySerializedFrame resp(
       spdy_util_.ConstructSpdyGetReply(headers, 1, 1));
   spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
@@ -4773,7 +4789,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -4884,6 +4901,8 @@
 
 // Same as above except for nested proxies where HTTP_1_1_REQUIRED is received
 // from the second proxy in the chain.
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 TEST_P(SpdyNetworkTransactionTest, HTTP11RequiredNestedProxySecondProxyRetry) {
   request_.method = "GET";
 
@@ -4891,7 +4910,8 @@
                                   HostPortPair("proxy1.test", 70)};
   const ProxyServer kProxyServer2{ProxyServer::SCHEME_HTTPS,
                                   HostPortPair("proxy2.test", 71)};
-  const ProxyChain kNestedProxyChain{{kProxyServer1, kProxyServer2}};
+  const ProxyChain kNestedProxyChain =
+      ProxyChain::ForIpProtection({{kProxyServer1, kProxyServer2}});
 
   ProxyList proxy_list;
   proxy_list.AddProxyChain(kNestedProxyChain);
@@ -5479,7 +5499,8 @@
   spdy::SpdySerializedFrame req2(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST));
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(req2, 2),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(req2, 2),
   };
 
   spdy::SpdySerializedFrame refused(
@@ -5545,8 +5566,10 @@
   spdy::SpdySerializedFrame req3(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 5, MEDIUM));
   MockWrite writes[] = {
-      MockWrite(ASYNC, ERR_IO_PENDING, 0), CreateMockWrite(req1, 1),
-      CreateMockWrite(req2, 5), CreateMockWrite(req3, 6),
+      MockWrite(ASYNC, ERR_IO_PENDING, 0),
+      CreateMockWrite(req1, 1),
+      CreateMockWrite(req2, 5),
+      CreateMockWrite(req3, 6),
   };
 
   spdy::SpdySerializedFrame resp1(
@@ -5650,7 +5673,8 @@
       spdy_util_.ConstructSpdyDataFrame(1, content, true));
 
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(body, 1),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(body, 1),
       CreateMockWrite(body_end, 2),
   };
 
@@ -5832,8 +5856,7 @@
   // Finish async network reads.
   base::RunLoop().RunUntilIdle();
 
-  SpdyHttpStream* stream =
-      static_cast<SpdyHttpStream*>(trans->stream_.get());
+  SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get());
   ASSERT_TRUE(stream);
   ASSERT_TRUE(stream->stream());
 
@@ -5879,7 +5902,8 @@
   // We're not going to write a data frame with FIN, we'll receive a bad
   // WINDOW_UPDATE while sending a request and will send a RST_STREAM frame.
   MockWrite writes[] = {
-      CreateMockWrite(req, 0), CreateMockWrite(body, 2),
+      CreateMockWrite(req, 0),
+      CreateMockWrite(body, 2),
       CreateMockWrite(rst, 3),
   };
 
@@ -6099,8 +6123,9 @@
   reads.push_back(CreateMockRead(window_update, i++));
 
   // Stalled frames which can be sent after receiving window updates.
-  if (last_body.size() > 0)
+  if (last_body.size() > 0) {
     writes.push_back(CreateMockWrite(body4, i++));
+  }
   writes.push_back(CreateMockWrite(body5, i++));
 
   spdy::SpdySerializedFrame reply(
@@ -6254,8 +6279,9 @@
   writes.push_back(CreateMockWrite(settings_ack, i++));
 
   // Stalled frames which can be sent after |settings_ack|.
-  if (last_body.size() > 0)
+  if (last_body.size() > 0) {
     writes.push_back(CreateMockWrite(body4, i++));
+  }
   writes.push_back(CreateMockWrite(body5, i++));
 
   spdy::SpdySerializedFrame reply(
@@ -6416,8 +6442,9 @@
   writes.push_back(CreateMockWrite(settings_ack, i++));
 
   // Stalled frames which can be sent after |settings_ack|.
-  if (last_body.size() > 0)
+  if (last_body.size() > 0) {
     writes.push_back(CreateMockWrite(body4, i++));
+  }
   writes.push_back(CreateMockWrite(body5, i++));
 
   spdy::SpdySerializedFrame reply(
diff --git a/net/spdy/spdy_session_pool_unittest.cc b/net/spdy/spdy_session_pool_unittest.cc
index 9291f30..3bf6af19 100644
--- a/net/spdy/spdy_session_pool_unittest.cc
+++ b/net/spdy/spdy_session_pool_unittest.cc
@@ -54,10 +54,9 @@
 using base::trace_event::MemoryAllocatorDump;
 using net::test::IsError;
 using net::test::IsOk;
+using testing::ByRef;
 using testing::Contains;
 using testing::Eq;
-using testing::Contains;
-using testing::ByRef;
 
 namespace net {
 
@@ -205,8 +204,7 @@
  public:
   SessionOpeningDelegate(SpdySessionPool* spdy_session_pool,
                          const SpdySessionKey& key)
-      : spdy_session_pool_(spdy_session_pool),
-        key_(key) {}
+      : spdy_session_pool_(spdy_session_pool), key_(key) {}
 
   ~SessionOpeningDelegate() override = default;
 
@@ -251,7 +249,7 @@
 
   MockConnect connect_data(SYNCHRONOUS, OK);
   MockRead reads[] = {
-    MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
+      MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
   };
 
   StaticSocketDataProvider data(reads, base::span<MockWrite>());
@@ -426,7 +424,7 @@
 
   MockConnect connect_data(SYNCHRONOUS, OK);
   MockRead reads[] = {
-    MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
+      MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
   };
 
   StaticSocketDataProvider data(reads, base::span<MockWrite>());
@@ -576,7 +574,7 @@
 
   MockConnect connect_data(SYNCHRONOUS, OK);
   MockRead reads[] = {
-    MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
+      MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
   };
 
   StaticSocketDataProvider data1(reads, base::span<MockWrite>());
@@ -802,7 +800,8 @@
     std::string iplist;
     SpdySessionKey key;
   } test_hosts[] = {
-      {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
+      {"www.example.org", "192.168.0.1"},
+      {"mail.example.org", "192.168.0.1"},
   };
 
   // Populate the HostResolver cache.
@@ -967,7 +966,8 @@
     std::string iplist;
     SpdySessionKey key;
   } test_hosts[] = {
-      {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
+      {"www.example.org", "192.168.0.1"},
+      {"mail.example.org", "192.168.0.1"},
   };
 
   // Populate the HostResolver cache.
@@ -1464,7 +1464,8 @@
     std::string iplist;
     SpdySessionKey key;
   } test_hosts[] = {
-      {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"},
+      {"www.example.org", "192.168.0.1"},
+      {"mail.example.org", "192.168.0.1"},
   };
 
   // Populate the HostResolver cache.
@@ -1610,8 +1611,9 @@
   void OnRequestDeleted() {
     EXPECT_FALSE(invoked_);
     invoked_ = true;
-    if (request_deleted_callback_)
+    if (request_deleted_callback_) {
       std::move(request_deleted_callback_).Run();
+    }
     run_loop_.Quit();
   }
 
@@ -1864,13 +1866,15 @@
 
 // Tests the OnSSLConfigForServersChanged() method matches SpdySessions
 // containing proxy chains.
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 TEST_F(SpdySessionPoolTest, SSLConfigForServerChangedWithProxyChain) {
   const MockConnect connect_data(SYNCHRONOUS, OK);
   const MockRead reads[] = {
       MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
   };
 
-  ProxyChain proxy_chain({
+  auto proxy_chain = ProxyChain::ForIpProtection({
       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
                                          "proxya", 443),
       ProxyServer::FromSchemeHostAndPort(ProxyServer::Scheme::SCHEME_HTTPS,
diff --git a/pdf/ink/ink_modeled_shape_view.h b/pdf/ink/ink_modeled_shape_view.h
index addf236b..f721cdd 100644
--- a/pdf/ink/ink_modeled_shape_view.h
+++ b/pdf/ink/ink_modeled_shape_view.h
@@ -21,6 +21,7 @@
   virtual ~InkModeledShapeView() = default;
 
   virtual uint32_t RenderGroupCount() const = 0;
+  virtual uint32_t OutlineCount(uint32_t group_index) const = 0;
 
   // Gets the collection of all outline positions for the 0-based render group
   // index.
diff --git a/pdf/ink/stub/ink_modeled_shape_view_stub.cc b/pdf/ink/stub/ink_modeled_shape_view_stub.cc
index 73ffdf68..d333956 100644
--- a/pdf/ink/stub/ink_modeled_shape_view_stub.cc
+++ b/pdf/ink/stub/ink_modeled_shape_view_stub.cc
@@ -14,6 +14,10 @@
   return 0;
 }
 
+uint32_t InkModeledShapeViewStub::OutlineCount(uint32_t group_index) const {
+  return 0;
+}
+
 std::vector<InkModeledShapeView::OutlinePositions>
 InkModeledShapeViewStub::GetRenderGroupOutlinePositions(
     uint32_t group_index) const {
diff --git a/pdf/ink/stub/ink_modeled_shape_view_stub.h b/pdf/ink/stub/ink_modeled_shape_view_stub.h
index 59d6be9..f0edd86 100644
--- a/pdf/ink/stub/ink_modeled_shape_view_stub.h
+++ b/pdf/ink/stub/ink_modeled_shape_view_stub.h
@@ -18,6 +18,7 @@
 
   // InkModeledShapeView:
   uint32_t RenderGroupCount() const override;
+  uint32_t OutlineCount(uint32_t group_index) const override;
   std::vector<OutlinePositions> GetRenderGroupOutlinePositions(
       uint32_t group_index) const override;
   InkRect Bounds() const override;
diff --git a/printing/backend/cups_printer.cc b/printing/backend/cups_printer.cc
index df1be80..d50bc983 100644
--- a/printing/backend/cups_printer.cc
+++ b/printing/backend/cups_printer.cc
@@ -22,6 +22,7 @@
 #include "printing/backend/cups_connection.h"
 #include "printing/backend/cups_ipp_constants.h"
 #include "printing/backend/cups_ipp_helper.h"
+#include "printing/backend/cups_weak_functions.h"
 #include "printing/backend/print_backend.h"
 #include "printing/backend/print_backend_consts.h"
 #include "printing/backend/print_backend_utils.h"
diff --git a/printing/backend/cups_weak_functions.h b/printing/backend/cups_weak_functions.h
index 7bba766b..a2b2a3a6 100644
--- a/printing/backend/cups_weak_functions.h
+++ b/printing/backend/cups_weak_functions.h
@@ -12,5 +12,7 @@
 
 WEAK_CUPS_FN(httpConnect2);
 
-// This may be removed when Amazon Linux 2 reaches EOL (30 Jun 2025).
+// These may be removed when Amazon Linux 2 reaches EOL (30 Jun 2025).
+WEAK_CUPS_FN(cupsFindDestDefault);
+WEAK_CUPS_FN(cupsFindDestSupported);
 WEAK_CUPS_FN(ippValidateAttributes);
diff --git a/printing/backend/print_backend_cups.cc b/printing/backend/print_backend_cups.cc
index 6ba91cb..2b93399 100644
--- a/printing/backend/print_backend_cups.cc
+++ b/printing/backend/print_backend_cups.cc
@@ -63,6 +63,13 @@
   return 1;  // Keep going.
 }
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
+// This may be removed when Amazon Linux 2 reaches EOL (30 Jun 2025).
+bool AreNewerCupsFunctionsAvailable() {
+  return cupsFindDestDefault && cupsFindDestSupported && ippValidateAttributes;
+}
+#endif
+
 }  // namespace
 
 PrintBackendCUPS::PrintBackendCUPS(const GURL& print_server_url,
@@ -285,9 +292,7 @@
 scoped_refptr<PrintBackend> PrintBackend::CreateInstanceImpl(
     const std::string& locale) {
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
-  // The `ippValidateAttributes` check may be removed when Amazon Linux 2
-  // reaches EOL (30 Jun 2025).
-  if (ippValidateAttributes &&
+  if (AreNewerCupsFunctionsAvailable() &&
       base::FeatureList::IsEnabled(features::kCupsIppPrintingBackend)) {
     return base::MakeRefCounted<PrintBackendCupsIpp>(CupsConnection::Create());
   }
diff --git a/services/network/ip_protection/ip_protection_proxy_delegate_unittest.cc b/services/network/ip_protection/ip_protection_proxy_delegate_unittest.cc
index 8aa83c6..f48a8ec 100644
--- a/services/network/ip_protection/ip_protection_proxy_delegate_unittest.cc
+++ b/services/network/ip_protection/ip_protection_proxy_delegate_unittest.cc
@@ -816,8 +816,10 @@
       "NetworkService.IpProtection.ProxyChainFallback", kChainId, 1);
 }
 
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 TEST_F(IpProtectionProxyDelegateTest, MergeProxyRules) {
-  net::ProxyChain chain1({
+  net::ProxyChain chain1 = net::ProxyChain::ForIpProtection({
       net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_HTTPS,
                                               "proxy2a.com", 80),
       net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_HTTPS,
@@ -831,7 +833,7 @@
   existing_proxy_list.AddProxyChain(chain2);
   existing_proxy_list.AddProxyChain(chain3);
 
-  net::ProxyChain custom1({
+  net::ProxyChain custom1 = net::ProxyChain::ForIpProtection({
       net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_HTTPS,
                                               "custom-a.com", 80),
       net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_HTTPS,
diff --git a/services/network/public/cpp/network_param_mojom_traits_unittest.cc b/services/network/public/cpp/network_param_mojom_traits_unittest.cc
index ff5f4b3..fa8b8a4 100644
--- a/services/network/public/cpp/network_param_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/network_param_mojom_traits_unittest.cc
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "services/network/public/cpp/network_param_mojom_traits.h"
+
 #include <vector>
 
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "net/base/proxy_chain.h"
 #include "net/base/proxy_server.h"
-#include "services/network/public/cpp/network_param_mojom_traits.h"
 #include "services/network/public/mojom/network_param.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -21,12 +22,14 @@
   EXPECT_EQ(original, copied);
 }
 
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 TEST(ProxyChain, SerializeAndDeserialize) {
   const net::ProxyChain kChains[] = {
       net::ProxyChain::Direct(),
       net::ProxyChain(net::ProxyServer::FromSchemeHostAndPort(
           net::ProxyServer::SCHEME_HTTPS, "foo1", 80)),
-      net::ProxyChain({
+      net::ProxyChain::ForIpProtection({
           net::ProxyServer::FromSchemeHostAndPort(
               net::ProxyServer::SCHEME_HTTPS, "foo1", 80),
           net::ProxyServer::FromSchemeHostAndPort(
diff --git a/services/on_device_model/fake/fake_chrome_ml_api.cc b/services/on_device_model/fake/fake_chrome_ml_api.cc
index d47a97a..a88a630 100644
--- a/services/on_device_model/fake/fake_chrome_ml_api.cc
+++ b/services/on_device_model/fake/fake_chrome_ml_api.cc
@@ -178,9 +178,16 @@
   return true;
 }
 
-void SessionSizeInTokens(ChromeMLSession session,
-                         const std::string& text,
-                         const ChromeMLSizeInTokensFn& fn) {
+void SessionSizeInTokensInputPiece(ChromeMLSession session,
+                                   ChromeMLModel model,
+                                   const ml::InputPiece* input,
+                                   size_t input_size,
+                                   const ChromeMLSizeInTokensFn& fn) {
+  std::string text;
+  for (size_t i = 0; i < input_size; i++) {
+    // SAFETY: `input_size` describes how big `input` is.
+    text += UNSAFE_BUFFERS(PieceToString(input[i]));
+  }
   fn(text.size());
 }
 
@@ -232,7 +239,7 @@
 
     .SessionCreateModel = &SessionCreateModel,
     .SessionExecuteModel = &SessionExecuteModel,
-    .SessionSizeInTokens = &SessionSizeInTokens,
+    .SessionSizeInTokensInputPiece = &SessionSizeInTokensInputPiece,
     .SessionScore = &SessionScore,
     .CreateSession = &CreateSession,
     .CloneSession = &CloneSession,
diff --git a/services/on_device_model/ml/chrome_ml_api.h b/services/on_device_model/ml/chrome_ml_api.h
index 3fbbf59..3c80b4b 100644
--- a/services/on_device_model/ml/chrome_ml_api.h
+++ b/services/on_device_model/ml/chrome_ml_api.h
@@ -342,6 +342,11 @@
   void (*SessionSizeInTokens)(ChromeMLSession session,
                               const std::string& text,
                               const ChromeMLSizeInTokensFn& fn);
+  void (*SessionSizeInTokensInputPiece)(ChromeMLSession session,
+                                        ChromeMLModel model,
+                                        const ml::InputPiece* input,
+                                        size_t input_size,
+                                        const ChromeMLSizeInTokensFn& fn);
 
   // Scores the first token of the given text.
   void (*SessionScore)(ChromeMLSession session,
diff --git a/services/on_device_model/ml/on_device_model_executor.cc b/services/on_device_model/ml/on_device_model_executor.cc
index f8f020ce..667177ab 100644
--- a/services/on_device_model/ml/on_device_model_executor.cc
+++ b/services/on_device_model/ml/on_device_model_executor.cc
@@ -334,9 +334,10 @@
 }
 
 DISABLE_CFI_DLSYM
-void SessionImpl::SizeInTokens(const std::string& text,
+void SessionImpl::SizeInTokens(on_device_model::mojom::InputPtr input,
                                base::OnceCallback<void(uint32_t)> callback) {
-  session_->SizeInTokens(text, ConvertCallbackToFn(std::move(callback)));
+  session_->SizeInTokens(std::move(input),
+                         ConvertCallbackToFn(std::move(callback)));
 }
 
 DISABLE_CFI_DLSYM
diff --git a/services/on_device_model/ml/on_device_model_executor.h b/services/on_device_model/ml/on_device_model_executor.h
index 324566e8..f49b637 100644
--- a/services/on_device_model/ml/on_device_model_executor.h
+++ b/services/on_device_model/ml/on_device_model_executor.h
@@ -51,7 +51,7 @@
       on_device_model::mojom::InputOptionsPtr input,
       mojo::PendingRemote<on_device_model::mojom::StreamingResponder> response,
       base::OnceClosure on_complete);
-  void SizeInTokens(const std::string& text,
+  void SizeInTokens(on_device_model::mojom::InputPtr input,
                     base::OnceCallback<void(uint32_t)> callback);
   void Score(const std::string& text, base::OnceCallback<void(float)> callback);
   std::unique_ptr<SessionImpl> Clone();
diff --git a/services/on_device_model/ml/session_accessor.cc b/services/on_device_model/ml/session_accessor.cc
index 235e84f..7be0523 100644
--- a/services/on_device_model/ml/session_accessor.cc
+++ b/services/on_device_model/ml/session_accessor.cc
@@ -93,12 +93,12 @@
                      text, std::move(score_fn)));
 }
 
-void SessionAccessor::SizeInTokens(const std::string& text,
+void SessionAccessor::SizeInTokens(on_device_model::mojom::InputPtr input,
                                    ChromeMLSizeInTokensFn size_in_tokens_fn) {
-  task_runner_->PostTask(FROM_HERE,
-                         base::BindOnce(&SessionAccessor::SizeInTokensInternal,
-                                        base::Unretained(this), text,
-                                        std::move(size_in_tokens_fn)));
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&SessionAccessor::SizeInTokensInternal,
+                                base::Unretained(this), std::move(input),
+                                std::move(size_in_tokens_fn)));
 }
 
 DISABLE_CFI_DLSYM
@@ -168,10 +168,12 @@
 
 DISABLE_CFI_DLSYM
 void SessionAccessor::SizeInTokensInternal(
-    const std::string& text,
+    on_device_model::mojom::InputPtr input,
     ChromeMLSizeInTokensFn size_in_tokens_fn) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  chrome_ml_->api().SessionSizeInTokens(session_, text, size_in_tokens_fn);
+  chrome_ml_->api().SessionSizeInTokensInputPiece(
+      session_, model_, input->pieces.data(), input->pieces.size(),
+      size_in_tokens_fn);
 }
 
 }  // namespace ml
diff --git a/services/on_device_model/ml/session_accessor.h b/services/on_device_model/ml/session_accessor.h
index 2bcdb792..370581a 100644
--- a/services/on_device_model/ml/session_accessor.h
+++ b/services/on_device_model/ml/session_accessor.h
@@ -38,7 +38,7 @@
                            ChromeMLExecutionOutputFn output_fn,
                            ChromeMLContextSavedFn context_saved_fn);
   void Score(const std::string& text, ChromeMLScoreFn score_fn);
-  void SizeInTokens(const std::string& text,
+  void SizeInTokens(on_device_model::mojom::InputPtr input,
                     ChromeMLSizeInTokensFn size_in_tokens_fn);
 
  private:
@@ -55,7 +55,7 @@
                        ChromeMLContextSavedFn context_saved_fn,
                        scoped_refptr<Canceler> canceler);
   void ScoreInternal(const std::string& text, ChromeMLScoreFn score_fn);
-  void SizeInTokensInternal(const std::string& text,
+  void SizeInTokensInternal(on_device_model::mojom::InputPtr input,
                             ChromeMLSizeInTokensFn size_in_tokens_fn);
 
   const raw_ref<const ChromeML> chrome_ml_;
diff --git a/services/on_device_model/on_device_model_service.cc b/services/on_device_model/on_device_model_service.cc
index 10b3489..f73d920a 100644
--- a/services/on_device_model/on_device_model_service.cc
+++ b/services/on_device_model/on_device_model_service.cc
@@ -45,7 +45,9 @@
   void Execute(
       mojom::InputOptionsPtr input,
       mojo::PendingRemote<mojom::StreamingResponder> response) override;
-  void GetSizeInTokens(const std::string& text,
+  void GetSizeInTokens(mojom::InputPtr input,
+                       GetSizeInTokensCallback callback) override;
+  void GetSizeInTokensDeprecated(const std::string& text,
                        GetSizeInTokensCallback callback) override;
   void Score(const std::string& text, ScoreCallback callback) override;
   void Clone(mojo::PendingReceiver<mojom::Session> session) override;
@@ -71,10 +73,10 @@
                       std::move(on_complete));
   }
 
-  void GetSizeInTokensInternal(const std::string& text,
+  void GetSizeInTokensInternal(mojom::InputPtr input,
                                GetSizeInTokensCallback callback,
                                base::OnceClosure on_complete) {
-    session_->SizeInTokens(text,
+    session_->SizeInTokens(std::move(input),
                            std::move(callback).Then(std::move(on_complete)));
   }
 
@@ -310,20 +312,27 @@
                                weak_ptr_factory_.GetWeakPtr());
 }
 
-void SessionWrapper::GetSizeInTokens(const std::string& text,
+void SessionWrapper::GetSizeInTokens(mojom::InputPtr input,
                                      GetSizeInTokensCallback callback) {
   if (!model_) {
     return;
   }
 
-  auto size_in_tokens_internal =
-      base::BindOnce(&SessionWrapper::GetSizeInTokensInternal,
-                     weak_ptr_factory_.GetWeakPtr(), text, std::move(callback));
+  auto size_in_tokens_internal = base::BindOnce(
+      &SessionWrapper::GetSizeInTokensInternal, weak_ptr_factory_.GetWeakPtr(),
+      std::move(input), std::move(callback));
 
   model_->AddAndRunPendingTask(std::move(size_in_tokens_internal),
                                weak_ptr_factory_.GetWeakPtr());
 }
 
+void SessionWrapper::GetSizeInTokensDeprecated(const std::string& text,
+                                     GetSizeInTokensCallback callback) {
+  auto input = mojom::Input::New();
+  input->pieces.push_back(text);
+  GetSizeInTokens(std::move(input), std::move(callback));
+}
+
 void SessionWrapper::Score(const std::string& text, ScoreCallback callback) {
   if (!model_) {
     return;
diff --git a/services/on_device_model/public/cpp/test_support/fake_service.cc b/services/on_device_model/public/cpp/test_support/fake_service.cc
index a6c0d1dd..f86bccd3 100644
--- a/services/on_device_model/public/cpp/test_support/fake_service.cc
+++ b/services/on_device_model/public/cpp/test_support/fake_service.cc
@@ -88,7 +88,12 @@
       settings_->execute_delay);
 }
 
-void FakeOnDeviceSession::GetSizeInTokens(const std::string& text,
+void FakeOnDeviceSession::GetSizeInTokensDeprecated(const std::string& text,
+                                          GetSizeInTokensCallback callback) {
+  std::move(callback).Run(0);
+}
+
+void FakeOnDeviceSession::GetSizeInTokens(mojom::InputPtr input,
                                           GetSizeInTokensCallback callback) {
   std::move(callback).Run(0);
 }
diff --git a/services/on_device_model/public/cpp/test_support/fake_service.h b/services/on_device_model/public/cpp/test_support/fake_service.h
index 014b250..0184462 100644
--- a/services/on_device_model/public/cpp/test_support/fake_service.h
+++ b/services/on_device_model/public/cpp/test_support/fake_service.h
@@ -81,7 +81,9 @@
       mojom::InputOptionsPtr input,
       mojo::PendingRemote<mojom::StreamingResponder> response) override;
 
-  void GetSizeInTokens(const std::string& text,
+  void GetSizeInTokensDeprecated(const std::string& text,
+                       GetSizeInTokensCallback callback) override;
+  void GetSizeInTokens(mojom::InputPtr input,
                        GetSizeInTokensCallback callback) override;
 
   void Score(const std::string& text, ScoreCallback callback) override;
diff --git a/services/on_device_model/public/mojom/on_device_model.mojom b/services/on_device_model/public/mojom/on_device_model.mojom
index ce371702..4da5eae8 100644
--- a/services/on_device_model/public/mojom/on_device_model.mojom
+++ b/services/on_device_model/public/mojom/on_device_model.mojom
@@ -177,7 +177,9 @@
 
   // Gets the size of the given text in tokens. Will return 0 if the text is
   // empty or error occurred.
-  GetSizeInTokens@2(string text) => (uint32 size);
+  [MinVersion=2]
+  GetSizeInTokens@5(Input input) => (uint32 size);
+  GetSizeInTokensDeprecated@2(string text) => (uint32 size);
 
   // Gets the probability score of the first token in `text` on top of the
   // current context.
diff --git a/services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits_unittests.cc b/services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits_unittests.cc
index 80e01529..62d17b0e 100644
--- a/services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits_unittests.cc
+++ b/services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits_unittests.cc
@@ -15,6 +15,8 @@
 
 namespace network {
 
+// TODO(crbug.com/365771838): Add tests for non-ip protection nested proxy
+// chains if support is enabled for all builds.
 TEST(ProxyInfo, SerializeAndDeserialize) {
   std::vector<net::ProxyInfo> infos;
 
@@ -28,7 +30,7 @@
   infos.push_back(std::move(single_proxy_chain));
 
   net::ProxyInfo multi_proxy_chain;
-  multi_proxy_chain.UseProxyChain(net::ProxyChain({
+  multi_proxy_chain.UseProxyChain(net::ProxyChain::ForIpProtection({
       net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_HTTPS,
                                               "foo1", 443),
       net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_HTTPS,
diff --git a/services/webnn/dml/graph_impl_dml.cc b/services/webnn/dml/graph_impl_dml.cc
index fdc1549e..2ef544b 100644
--- a/services/webnn/dml/graph_impl_dml.cc
+++ b/services/webnn/dml/graph_impl_dml.cc
@@ -4184,12 +4184,30 @@
            output_tensor_dims.size());
 
   const std::string& label = matmul->label;
-  // TODO(issues.chromium.org/353856233): Flatten adjacent dimensions for GEMM >
-  // 4D because DML_GEMM_OPERATOR_DESC restricts tensor's rank <= 4.
-  if (input_a_tensor_desc.GetDimensions().size() > 4) {
-    return CreateUnexpectedError(
-        mojom::Error::Code::kNotSupportedError,
-        "The input tensor rank is larger than 4 for matmul operator.", label);
+  // Flatten adjacent dimensions for GEMM > 4D because DML_GEMM_OPERATOR_DESC
+  // restricts tensor's rank <= 4.
+  auto adjusted_output_tensor_desc = output_tensor_desc;
+  if (output_tensor_dims.size() > 4) {
+    // If flattening fails due to the non-default strides caused by
+    // broadcasting, append an identity node after the input to consume the
+    // non-default strides, ensuring successful flattening of the input.
+    if (!input_a_tensor_desc.RightAlignedFlattenTo(4)) {
+      input_a_node_output = AppendIdentityNode(
+          graph_builder, input_a_node_output, &input_a_tensor_desc);
+      input_a_tensor_desc = input_a_node_output->GetTensorDesc();
+
+      CHECK(input_a_tensor_desc.RightAlignedFlattenTo(4));
+    }
+
+    if (!input_b_tensor_desc.RightAlignedFlattenTo(4)) {
+      input_b_node_output = AppendIdentityNode(
+          graph_builder, input_b_node_output, &input_b_tensor_desc);
+      input_b_tensor_desc = input_b_node_output->GetTensorDesc();
+
+      CHECK(input_b_tensor_desc.RightAlignedFlattenTo(4));
+    }
+
+    CHECK(adjusted_output_tensor_desc.RightAlignedFlattenTo(4));
   }
 
   // Use 4D GEMM which is available since feature level 1.0 for best
@@ -4198,11 +4216,10 @@
   // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_gemm_operator_desc.
   // TODO(issues.chromium.org/327244277): Remove the workaround of coercing
   // GEMM's tensors to 4D.
-  auto expanded_output_tensor_desc = output_tensor_desc;
-  if (output_tensor_dims.size() < 4) {
+  else if (output_tensor_dims.size() < 4) {
     input_a_tensor_desc.EnsureMinimumRank(4, TensorDesc::Alignment::kTrailing);
     input_b_tensor_desc.EnsureMinimumRank(4, TensorDesc::Alignment::kTrailing);
-    expanded_output_tensor_desc.EnsureMinimumRank(
+    adjusted_output_tensor_desc.EnsureMinimumRank(
         4, TensorDesc::Alignment::kTrailing);
   }
 
@@ -4224,7 +4241,7 @@
       .ATensor = &input_a_tensor_desc.GetDMLTensorDesc(),
       .BTensor = &input_b_tensor_desc.GetDMLTensorDesc(),
       .CTensor = nullptr,
-      .OutputTensor = &expanded_output_tensor_desc.GetDMLTensorDesc(),
+      .OutputTensor = &adjusted_output_tensor_desc.GetDMLTensorDesc(),
       .TransA = transpose_a ? DML_MATRIX_TRANSFORM_TRANSPOSE
                             : DML_MATRIX_TRANSFORM_NONE,
       .TransB = transpose_b ? DML_MATRIX_TRANSFORM_TRANSPOSE
diff --git a/services/webnn/dml/tensor_desc.cc b/services/webnn/dml/tensor_desc.cc
index 292a40d..adf182c 100644
--- a/services/webnn/dml/tensor_desc.cc
+++ b/services/webnn/dml/tensor_desc.cc
@@ -155,6 +155,47 @@
   }
 }
 
+bool TensorDesc::RightAlignedFlattenTo(size_t flattened_rank) {
+  auto flattened_dimensions = dimensions_;
+  auto flattened_strides = strides_;
+  auto original_rank = dimensions_.size();
+  if (flattened_rank == original_rank) {
+    return true;
+  }
+  CHECK_LT(flattened_rank, original_rank);
+  CHECK_NE(flattened_rank, 0u);
+
+  auto flattened_size = base::MakeCheckedNum<uint32_t>(1);
+  for (size_t i = 0; i < original_rank - flattened_rank + 1; ++i) {
+    flattened_size *= flattened_dimensions[i];
+  }
+  flattened_dimensions.erase(
+      flattened_dimensions.begin(),
+      flattened_dimensions.begin() + original_rank - flattened_rank);
+  flattened_dimensions[0] = flattened_size.ValueOrDie();
+
+  flattened_strides.erase(
+      flattened_strides.begin(),
+      flattened_strides.begin() + original_rank - flattened_rank);
+
+  // Flattening is invalid if the total tensor size in bytes after flattening
+  // does not equal the original size. This can occur when the original strides
+  // are non-default values due to broadcasting or transposing.
+  if (CalculateDMLBufferTensorSize(buffer_desc_.DataType, flattened_dimensions,
+                                   flattened_strides) !=
+      buffer_desc_.TotalTensorSizeInBytes) {
+    return false;
+  }
+  dimensions_ = std::move(flattened_dimensions);
+  strides_ = std::move(flattened_strides);
+
+  buffer_desc_.DimensionCount = dimensions_.size();
+  buffer_desc_.Sizes = dimensions_.data();
+  buffer_desc_.Strides = strides_.data();
+
+  return true;
+}
+
 void TensorDesc::EnsureMinimumRank(size_t minimum_rank, Alignment alignment) {
   if (dimensions_.size() >= minimum_rank) {
     return;
diff --git a/services/webnn/dml/tensor_desc.h b/services/webnn/dml/tensor_desc.h
index 7c713c10..6691b76 100644
--- a/services/webnn/dml/tensor_desc.h
+++ b/services/webnn/dml/tensor_desc.h
@@ -63,6 +63,23 @@
   void BroadcastTo(base::span<const uint32_t> broadcasted_dims,
                    size_t ignorable_tails_count = 0);
 
+  // Flatten the tensor from the leftmost dimensions to reduce its rank,
+  // aligning the right dimensions. The strides of the TensorDesc will be
+  // adjusted accordingly. The flattened rank must be greater than zero and not
+  // exceed the original rank.
+  // For example, a 5D tensor descriptor with dimensions [3, 4, 5, 6, 7] can be
+  // flattened to a 4D tensor descriptor with dimensions [12, 5, 6, 7] by
+  // folding the two leftmost dimensions [3, 4] to [12].
+  // Some tensor descriptors with non-default strides cannot be flattened.
+  // For example, if a tensor descriptor with dimensions [1, 2, 3] is
+  // broadcasted to [2, 2, 3], the resulting tensor descriptor cannot be
+  // flattened to 2D. Similarly, if a tensor descriptor with dimensions [5, 4,
+  // 3, 2, 1] is transposed to [4, 5, 3, 2, 1], the resulting tensor descriptor
+  // cannot be flattened to 4D.
+  // Appending an identity operator to consume the non-default strides in
+  // advance can ensure successful flattening.
+  bool RightAlignedFlattenTo(size_t flattened_rank);
+
   // Add leading or trailing ones to dimensions and zeros to strides to ensure
   // the tensor rank >= minimum_rank.
   void EnsureMinimumRank(size_t minimum_rank, Alignment alignment);
diff --git a/services/webnn/dml/tensor_desc_test.cc b/services/webnn/dml/tensor_desc_test.cc
index ccda7be..2c6a2af 100644
--- a/services/webnn/dml/tensor_desc_test.cc
+++ b/services/webnn/dml/tensor_desc_test.cc
@@ -125,6 +125,62 @@
   EXPECT_EQ(tensor.GetStrides(), (std::vector<uint32_t>{1, 6, 3}));
 }
 
+TEST_F(WebNNTensorDescTest, FlattenTensorDesc) {
+  std::vector<uint32_t> dimensions = {2, 2, 2, 2, 3};
+  TensorDesc tensor(DML_TENSOR_DATA_TYPE_FLOAT32, dimensions);
+  EXPECT_TRUE(tensor.RightAlignedFlattenTo(4));
+  EXPECT_EQ(tensor.GetDimensions(), (std::vector<uint32_t>{4, 2, 2, 3}));
+  EXPECT_EQ(tensor.GetStrides(), (std::vector<uint32_t>{12, 6, 3, 1}));
+}
+
+TEST_F(WebNNTensorDescTest, TransposeAndFlattenTensorDesc) {
+  {
+    std::vector<uint32_t> dimensions = {2, 2, 1, 2, 3};
+    TensorDesc tensor(DML_TENSOR_DATA_TYPE_FLOAT32, dimensions);
+    tensor.Transpose(std::vector<uint32_t>{0, 1, 2, 4, 3});
+    EXPECT_TRUE(tensor.RightAlignedFlattenTo(4));
+    EXPECT_EQ(tensor.GetDimensions(), (std::vector<uint32_t>{4, 1, 3, 2}));
+    EXPECT_EQ(tensor.GetStrides(), (std::vector<uint32_t>{6, 6, 1, 3}));
+  }
+  {
+    std::vector<uint32_t> dimensions = {2, 2, 1, 2, 3};
+    TensorDesc tensor(DML_TENSOR_DATA_TYPE_FLOAT32, dimensions);
+    tensor.Transpose(std::vector<uint32_t>{1, 0, 2, 3, 4});
+    EXPECT_TRUE(tensor.RightAlignedFlattenTo(3));
+    EXPECT_EQ(tensor.GetDimensions(), (std::vector<uint32_t>{4, 2, 3}));
+    EXPECT_EQ(tensor.GetStrides(), (std::vector<uint32_t>{6, 3, 1}));
+  }
+  // Test that some transposed tensor can't be flattened.
+  {
+    std::vector<uint32_t> dimensions = {5, 4, 3, 2, 1};
+    TensorDesc tensor(DML_TENSOR_DATA_TYPE_FLOAT32, dimensions);
+    tensor.Transpose(std::vector<uint32_t>{1, 0, 2, 3, 4});
+    EXPECT_FALSE(tensor.RightAlignedFlattenTo(4));
+    EXPECT_EQ(tensor.GetDimensions(), (std::vector<uint32_t>{4, 5, 3, 2, 1}));
+    EXPECT_EQ(tensor.GetStrides(), (std::vector<uint32_t>{6, 24, 2, 1, 1}));
+  }
+}
+
+TEST_F(WebNNTensorDescTest, BroadcastAndFlattenTensorDesc) {
+  {
+    std::vector<uint32_t> dimensions = {1, 1, 3};
+    TensorDesc tensor(DML_TENSOR_DATA_TYPE_FLOAT32, dimensions);
+    tensor.BroadcastTo(std::vector<uint32_t>{2, 2, 3});
+    EXPECT_TRUE(tensor.RightAlignedFlattenTo(2));
+    EXPECT_EQ(tensor.GetDimensions(), (std::vector<uint32_t>{4, 3}));
+    EXPECT_EQ(tensor.GetStrides(), (std::vector<uint32_t>{0, 1}));
+  }
+  // Test that some broadcasted tensor can't be flattened.
+  {
+    std::vector<uint32_t> dimensions = {1, 2, 3};
+    TensorDesc tensor(DML_TENSOR_DATA_TYPE_FLOAT32, dimensions);
+    tensor.BroadcastTo(std::vector<uint32_t>{2, 2, 3});
+    EXPECT_FALSE(tensor.RightAlignedFlattenTo(2));
+    EXPECT_EQ(tensor.GetDimensions(), (std::vector<uint32_t>{2, 2, 3}));
+    EXPECT_EQ(tensor.GetStrides(), (std::vector<uint32_t>{0, 3, 1}));
+  }
+}
+
 TEST_F(WebNNTensorDescTest, CreateAndBroadcastTensorDesc) {
   // Test creating a TensorDesc with dimensions and broadcasting it.
   std::vector<uint32_t> dimensions = {1, 1, 3};
diff --git a/sql/statement.cc b/sql/statement.cc
index cc22f60d..f2322cf 100644
--- a/sql/statement.cc
+++ b/sql/statement.cc
@@ -533,7 +533,7 @@
   return base::Microseconds(int_value);
 }
 
-std::string Statement::ColumnString(int column_index) {
+std::string_view Statement::ColumnStringView(int column_index) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
 #if DCHECK_IS_ON()
@@ -542,7 +542,7 @@
 #endif  // DCHECK_IS_ON()
 
   if (!CheckValid())
-    return std::string();
+    return std::string_view();
   DCHECK_GE(column_index, 0);
   DCHECK_LT(column_index, sqlite3_data_count(ref_->stmt()))
       << "Invalid column index";
@@ -550,29 +550,18 @@
   const char* string_buffer = reinterpret_cast<const char*>(
       sqlite3_column_text(ref_->stmt(), column_index));
   int size = sqlite3_column_bytes(ref_->stmt(), column_index);
+  DCHECK(size == 0 || string_buffer != nullptr)
+      << "sqlite3_column_text() returned a null buffer for a non-empty string";
 
-  std::string result;
-  if (string_buffer && size > 0)
-    result.assign(string_buffer, size);
-  return result;
+  return std::string_view(string_buffer, base::checked_cast<size_t>(size));
+}
+
+std::string Statement::ColumnString(int column_index) {
+  return std::string(ColumnStringView(column_index));
 }
 
 std::u16string Statement::ColumnString16(int column_index) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-#if DCHECK_IS_ON()
-  DCHECK(!run_called_) << __func__ << " can be used after Step(), not Run()";
-  DCHECK(step_called_) << __func__ << " can only be used after Step()";
-#endif  // DCHECK_IS_ON()
-
-  if (!CheckValid())
-    return std::u16string();
-  DCHECK_GE(column_index, 0);
-  DCHECK_LT(column_index, sqlite3_data_count(ref_->stmt()))
-      << "Invalid column index";
-
-  std::string string = ColumnString(column_index);
-  return string.empty() ? std::u16string() : base::UTF8ToUTF16(string);
+  return base::UTF8ToUTF16(ColumnStringView(column_index));
 }
 
 base::span<const uint8_t> Statement::ColumnBlob(int column_index) {
diff --git a/sql/statement.h b/sql/statement.h
index 155c002..602bb7e4 100644
--- a/sql/statement.h
+++ b/sql/statement.h
@@ -203,6 +203,19 @@
   // `ColumnBlobAsString16()`.
   std::u16string ColumnString16(int column_index);
 
+  // Returns a string view pointing to a buffer containing the string data.
+  //
+  // This can be used to avoid allocating a temporary string when the value is
+  // immediately passed to a function accepting a string view. Otherwise, the
+  // string view's contents should be copied to a caller-owned buffer
+  // immediately. Any method call on the `Statement` may invalidate the string
+  // view.
+  //
+  // The string view will be empty (and may have a null data) if the underlying
+  // string is empty. Code that needs to distinguish between empty strings and
+  // NULL should call `GetColumnType()` before calling `ColumnStringView()`.
+  std::string_view ColumnStringView(int column_index);
+
   // Conforms with base::Time serialization recommendations.
   //
   // This is equivalent to the following snippets, which should be replaced.
diff --git a/sql/statement_unittest.cc b/sql/statement_unittest.cc
index 83b50ac..6ef656ab 100644
--- a/sql/statement_unittest.cc
+++ b/sql/statement_unittest.cc
@@ -320,12 +320,23 @@
     insert.Reset(/*clear_bound_vars=*/true);
   }
 
-  Statement select(db_.GetUniqueStatement("SELECT t FROM texts ORDER BY id"));
-  for (const std::string& value : values) {
-    ASSERT_TRUE(select.Step());
-    EXPECT_EQ(value, select.ColumnString(0));
+  {
+    Statement select(db_.GetUniqueStatement("SELECT t FROM texts ORDER BY id"));
+    for (const std::string& value : values) {
+      ASSERT_TRUE(select.Step());
+      EXPECT_EQ(value, select.ColumnString(0));
+    }
+    EXPECT_FALSE(select.Step());
   }
-  EXPECT_FALSE(select.Step());
+
+  {
+    Statement select(db_.GetUniqueStatement("SELECT t FROM texts ORDER BY id"));
+    for (const std::string& value : values) {
+      ASSERT_TRUE(select.Step());
+      EXPECT_EQ(value, select.ColumnStringView(0));
+    }
+    EXPECT_FALSE(select.Step());
+  }
 }
 
 TEST_F(StatementTest, BindString_NullData) {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 1eb640c3..e66f295 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -14508,156 +14508,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPhone 14 16.4",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_16_4",
-              "path": "Runtime-ios-16.4"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 16.4"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "17.5",
-          "--output-disabled-tests",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 14 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 15",
-          "--version",
-          "18.0",
-          "--output-disabled-tests",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 15 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 15 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "16.4",
-          "--output-disabled-tests",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPhone 14 16.4",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 7f1f1f6..bb25d72 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -13053,102 +13053,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPhone 14 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 15",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 15 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 15 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "17.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPhone 14 17.5",
         "resultdb": {
           "enable": true,
@@ -19447,206 +19351,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPad Air (5th generation) 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPad Air (5th generation) 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 11-inch (M2)",
-          "--version",
-          "18.0",
-          "--args-json",
-          "{\"test_args\": [\"--run-with-custom-webkit\"]}",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPad Air (6th generation) 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPad Air (6th generation) 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "17.5",
-          "--args-json",
-          "{\"test_args\": [\"--run-with-custom-webkit\"]}",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 14 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 15",
-          "--version",
-          "18.0",
-          "--args-json",
-          "{\"test_args\": [\"--run-with-custom-webkit\"]}",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 15 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 15 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air (5th generation)",
-          "--version",
-          "17.5",
-          "--args-json",
-          "{\"test_args\": [\"--run-with-custom-webkit\"]}",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPad Air (5th generation) 17.5",
         "resultdb": {
           "enable": true,
@@ -24287,102 +23991,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPad Air (5th generation) 18.1",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_1",
-              "path": "Runtime-ios-18.1"
-            },
-            {
-              "name": "xcode_ios_16b5001e",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPad Air (5th generation) 18.1"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "18.1",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16b5001e",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 14 18.1",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_1",
-              "path": "Runtime-ios-18.1"
-            },
-            {
-              "name": "xcode_ios_16b5001e",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 18.1"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air (5th generation)",
-          "--version",
-          "18.1",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16b5001e",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPad Air (5th generation) 18.1",
         "resultdb": {
           "enable": true,
@@ -27897,102 +27505,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPad Air (5th generation) 18.1",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_1",
-              "path": "Runtime-ios-18.1"
-            },
-            {
-              "name": "xcode_ios_16b5001e",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPad Air (5th generation) 18.1"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "18.1",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16b5001e",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 14 18.1",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_1",
-              "path": "Runtime-ios-18.1"
-            },
-            {
-              "name": "xcode_ios_16b5001e",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 18.1"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air (5th generation)",
-          "--version",
-          "18.1",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16b5001e",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPad Air (5th generation) 18.1",
         "resultdb": {
           "enable": true,
@@ -32066,54 +31578,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPhone 15 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14.5"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a242",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 15 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 15",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a242",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPhone 15 18.0",
         "resultdb": {
           "enable": true,
@@ -36409,102 +35873,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPhone 14 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14.5"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a242",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 15",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a242",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 15 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "arm64",
-            "os": "Mac-14.5"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a242",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 15 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "17.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a242",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPhone 14 17.5",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index f345295..1dd5387 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -6871,104 +6871,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPhone 14 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 15",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 15 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 15 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "17.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPhone 14 17.5",
         "resultdb": {
           "enable": true,
@@ -11866,104 +11768,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPhone 14 Plus 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 Plus 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14 Plus",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 14 Plus 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 Plus 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14 Plus",
-          "--version",
-          "17.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPhone 14 Plus 17.5",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 0275c19..eb33771 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -14048,102 +14048,6 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "ios_remoting_unittests iPad Air (6th generation) 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPad Air (6th generation) 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 15",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 15 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 15 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 11-inch (M2)",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
         "name": "ios_testing_unittests iPad Air (6th generation) 18.0",
         "resultdb": {
           "enable": true,
diff --git a/testing/buildbot/chromium.webrtc.fyi.json b/testing/buildbot/chromium.webrtc.fyi.json
index b0d6e24..c21baf3 100644
--- a/testing/buildbot/chromium.webrtc.fyi.json
+++ b/testing/buildbot/chromium.webrtc.fyi.json
@@ -590,200 +590,5 @@
     ]
   },
   "WebRTC Chromium FYI ios-device": {},
-  "WebRTC Chromium FYI ios-simulator": {
-    "isolated_scripts": [
-      {
-        "args": [
-          "--platform",
-          "iPad Air (5th generation)",
-          "--version",
-          "17.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPad Air (5th generation) 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPad Air (5th generation) 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 11-inch (M2)",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPad Air (6th generation) 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPad Air (6th generation) 18.0"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 14",
-          "--version",
-          "17.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 14 17.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_17_5",
-              "path": "Runtime-ios-17.5"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 14 17.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 15",
-          "--version",
-          "18.0",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "16a5230g",
-          "--xctest"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone 15 18.0",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:a18b7d95d26f3c6bf9591978b19cf0ca8268ac7d"
-            }
-          ],
-          "dimensions": {
-            "cpu": "x86-64",
-            "os": "Mac-14"
-          },
-          "named_caches": [
-            {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
-            },
-            {
-              "name": "xcode_ios_16a5230g",
-              "path": "Xcode.app"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ios_remoting_unittests",
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone 15 18.0"
-      }
-    ]
-  }
+  "WebRTC Chromium FYI ios-simulator": {}
 }
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index b074acf..b4f3eaf 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -5418,10 +5418,7 @@
           'out_dir_arg',
           'xcode_16_main',
           'xctest',
-        ],
-        'test_suites': {
-          'isolated_scripts': 'ios_webrtc_fyi_tests',
-        },
+        ]
       },
     },
   },
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 8c9e97f2a..63f7e95e 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -590,7 +590,15 @@
 
     if not no_output_conversion:
       expected_perf_filename = os.path.join(temp_dir, 'histograms.json')
-      shutil.move(expected_perf_filename, output_paths.perf_results)
+      if os.path.exists(expected_perf_filename):
+        shutil.move(expected_perf_filename, output_paths.perf_results)
+      elif return_code:
+        print(f'The benchmark failed with status code {return_code}, '
+              'and did not produce perf results output. '
+              'Check benchmark output for more details.')
+      else:
+        print('The benchmark returned a success status code, '
+              'but did not product perf results output.')
 
     csv_file_path = os.path.join(temp_dir, 'results.csv')
     if os.path.isfile(csv_file_path):
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index dcb6977..d2562a8 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1302,71 +1302,6 @@
             ]
         }
     ],
-    "AttributionReportDeliveryRetryDelays": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_CurrentDelay",
-                    "params": {
-                        "first_retry_delay": "5m",
-                        "second_retry_delay": "15m"
-                    },
-                    "enable_features": [
-                        "AttributionReportDeliveryRetryDelays"
-                    ]
-                },
-                {
-                    "name": "Enabled_SmallDelay",
-                    "params": {
-                        "first_retry_delay": "2m",
-                        "second_retry_delay": "10m"
-                    },
-                    "enable_features": [
-                        "AttributionReportDeliveryRetryDelays"
-                    ]
-                },
-                {
-                    "name": "Enabled_MediumDelay",
-                    "params": {
-                        "first_retry_delay": "10m",
-                        "second_retry_delay": "1h"
-                    },
-                    "enable_features": [
-                        "AttributionReportDeliveryRetryDelays"
-                    ]
-                },
-                {
-                    "name": "Enabled_LargeDelay",
-                    "params": {
-                        "first_retry_delay": "15m",
-                        "second_retry_delay": "6h"
-                    },
-                    "enable_features": [
-                        "AttributionReportDeliveryRetryDelays"
-                    ]
-                },
-                {
-                    "name": "Enabled_XLargeDelay",
-                    "params": {
-                        "first_retry_delay": "20m",
-                        "second_retry_delay": "24h"
-                    },
-                    "enable_features": [
-                        "AttributionReportDeliveryRetryDelays"
-                    ]
-                }
-            ]
-        }
-    ],
     "AudioFocusEnforcementStudy": [
         {
             "platforms": [
@@ -5288,6 +5223,25 @@
             ]
         }
     ],
+    "ChromnientPostLaunchTranslate": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "TranslateButtonEnabled",
+                    "enable_features": [
+                        "LensOverlayTranslateButton"
+                    ]
+                }
+            ]
+        }
+    ],
     "ClankFeedSyntheticCapabilities": [
         {
             "platforms": [
@@ -13757,6 +13711,16 @@
                     ]
                 },
                 {
+                    "name": "ManagedDisabled",
+                    "enable_features": [
+                        "Mahi",
+                        "MediaAppPdfMahi"
+                    ],
+                    "disable_features": [
+                        "MahiManaged"
+                    ]
+                },
+                {
                     "name": "Disabled",
                     "disable_features": [
                         "Mahi",
@@ -21392,6 +21356,27 @@
             ]
         }
     ],
+    "ScriptedIdleTaskControllerOOMFix": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ScriptedIdleTaskControllerOOMFix"
+                    ]
+                }
+            ]
+        }
+    ],
     "SeaPen": [
         {
             "platforms": [
@@ -22632,24 +22617,6 @@
             ]
         }
     ],
-    "SuppressToolbarCaptures": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "SuppressionBlockFullscreen_20231004",
-                    "params": {
-                        "block_for_fullscreen": "true"
-                    },
-                    "enable_features": [
-                        "SuppressToolbarCaptures"
-                    ]
-                }
-            ]
-        }
-    ],
     "SuppressToolbarCapturesAtGestureEndStudy": [
         {
             "platforms": [
@@ -22657,25 +22624,10 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "Enabled_BothSuppressToolbarCaptures",
                     "enable_features": [
                         "EnableGestureBeginEndTypes",
-                        "SuppressToolbarCapturesAtGestureEnd"
-                    ]
-                },
-                {
-                    "name": "Disabled",
-                    "disable_features": [
-                        "EnableGestureBeginEndTypes",
-                        "SuppressToolbarCapturesAtGestureEnd"
-                    ]
-                },
-                {
-                    "name": "Enabled_GestureBeginEndTypes",
-                    "enable_features": [
-                        "EnableGestureBeginEndTypes"
-                    ],
-                    "disable_features": [
+                        "SuppressToolbarCaptures",
                         "SuppressToolbarCapturesAtGestureEnd"
                     ]
                 }
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 4aa68a3..138e8df 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -37,6 +37,7 @@
 /chromeos_login_manager
 /chromeos_text_input
 /cld_2/src
+/cronet_android_mainline_clang/linux-amd64
 /cros
 /custom_tabs_client/src
 /cygwin
@@ -65,8 +66,8 @@
 /jdk/current/
 /jdk/extras/java_8
 /jdk11/current
-/js_code_coverage/.*
 /js_code_coverage/*.tar.gz
+/js_code_coverage/.*
 /js_code_coverage/node_modules/
 /jsr-305/src
 /kotlinc/current
@@ -83,9 +84,8 @@
 /llvm-allocated-type
 /llvm-bootstrap
 /llvm-bootstrap-install
-/llvm-build/
 /llvm-build-tools
-/cronet_android_mainline_clang/linux-amd64
+/llvm-build/
 /mesa/src
 /microsoft_dxheaders/src
 /mingw-w64
@@ -119,8 +119,8 @@
 /qemu-linux-x64
 /qemu-mac-x64
 /rust-src
-/rust-toolchain/
 /rust-toolchain-intermediate
+/rust-toolchain/
 /scan-build/src
 /scons-2.0.1
 /screen-ai
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 4d1d2e8..1879894 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -107,11 +107,6 @@
   }
 }
 
-java_annotation_processor("auto_service_processor") {
-  main_class = "com.google.auto.service.processor.AutoServiceProcessor"
-  deps = [ ":com_google_auto_service_auto_service_java" ]
-}
-
 # The section below is generated by running
 # `//tools/android/roll/android_deps/fetch_all.py`
 
@@ -154,27 +149,6 @@
 }
 
 # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-java_prebuilt("com_google_auto_auto_common_java") {
-  jar_path = "cipd/libs/com_google_auto_auto_common/auto-common-1.2.1.jar"
-  output_name = "com_google_auto_auto_common"
-  enable_bytecode_checks = false
-  deps = [ ":com_google_guava_guava_java" ]
-}
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-java_prebuilt("com_google_auto_service_auto_service_java") {
-  jar_path =
-      "cipd/libs/com_google_auto_service_auto_service/auto-service-1.0-rc6.jar"
-  output_name = "com_google_auto_service_auto_service"
-  enable_bytecode_checks = false
-  deps = [
-    ":com_google_auto_auto_common_java",
-    ":com_google_auto_service_auto_service_annotations_java",
-    ":com_google_guava_guava_java",
-  ]
-}
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
 java_prebuilt("com_google_auto_service_auto_service_annotations_java") {
   jar_path = "cipd/libs/com_google_auto_service_auto_service_annotations/auto-service-annotations-1.0-rc6.jar"
   output_name = "com_google_auto_service_auto_service_annotations"
diff --git a/third_party/android_deps/additional_readme_paths.json b/third_party/android_deps/additional_readme_paths.json
index 69fca96..f4ad42d 100644
--- a/third_party/android_deps/additional_readme_paths.json
+++ b/third_party/android_deps/additional_readme_paths.json
@@ -29,8 +29,6 @@
     "libs/com_google_android_material_material",
     "libs/com_google_android_play_core_common",
     "libs/com_google_android_play_feature_delivery",
-    "libs/com_google_auto_auto_common",
-    "libs/com_google_auto_service_auto_service",
     "libs/com_google_auto_service_auto_service_annotations",
     "libs/com_google_auto_value_auto_value_annotations",
     "libs/com_google_code_findbugs_jsr305",
@@ -88,6 +86,7 @@
     "libs/org_jetbrains_kotlinx_kotlinx_coroutines_android",
     "libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm",
     "libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava",
+    "libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm",
     "libs/org_jsoup_jsoup",
     "libs/org_mockito_mockito_android",
     "libs/org_mockito_mockito_core",
diff --git a/third_party/android_deps/build.gradle b/third_party/android_deps/build.gradle
index 85e9d08..3ae7ec6 100644
--- a/third_party/android_deps/build.gradle
+++ b/third_party/android_deps/build.gradle
@@ -86,8 +86,9 @@
     compile 'com.google.android.gms:play-services-vision-common:19.1.3'
     compile 'com.google.android.gms:play-services-vision:20.1.3'
 
-    // Still needed by internal targets.
+    // Needed only by internal targets:
     compile "com.android.support:support-annotations:28.0.0"
+    buildCompile 'com.google.auto.service:auto-service-annotations:1.0-rc6'
 
     compile 'com.google.code.findbugs:jsr305:3.0.2'
     compile 'com.google.firebase:firebase-iid:21.0.1'
@@ -112,11 +113,14 @@
     compile 'org.jetbrains.kotlinx:atomicfu-jvm:0.23.2'
     // Needed by androidx and by doubledown targets.
     // Note: These have version overrides set in ChromiumDepGraph.groovy.
-    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4"
-    compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
-    compile "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.4"
+    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.1"
+    compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"
+    compile "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.8.1"
+    compile "org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.2"
     compile "org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.22"
 
+    // Needed by androidx.datastore
+    compile "com.squareup.okio:okio-jvm:3.9.0"
     // Needed by androidx.macrobenchmarks
     androidTestCompile "com.squareup.wire:wire-runtime-jvm:4.4.3"
     // Needed by androidx.benchmark
@@ -143,9 +147,6 @@
     compile 'org.checkerframework:checker-util:3.25.0'
     compile 'org.codehaus.mojo:animal-sniffer-annotations:1.17'
 
-    buildCompile 'com.google.auto:auto-common:1.2.1'
-    buildCompile 'com.google.auto.service:auto-service:1.0-rc6'
-    buildCompile 'com.google.auto.service:auto-service-annotations:1.0-rc6'
     buildCompile 'com.google.code.gson:gson:2.8.0'
     buildCompile 'org.ow2.asm:asm:7.0'
     buildCompile 'org.ow2.asm:asm-commons:7.0'
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index 7071bc8..a3ebee3 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -805,6 +805,14 @@
                 // and android_aar_prebuilt template will fail if it's not set explictly.
                 sb.append('  extract_native_libraries = true\n')
                 break
+            case 'com_google_dagger_hilt_core':
+                sb.append('\n')
+                sb.append('  # Google3 organizes targets differently from maven. Restrict to the only classes we use.\n')
+                sb.append('  jar_included_patterns = [\n')
+                sb.append('    "dagger/hilt/internal/GeneratedComponentManager.class",\n')
+                sb.append('    "dagger/hilt/internal/GeneratedComponentManagerHolder.class",\n')
+                sb.append('  ]\n')
+                break
             case 'com_google_auto_service_auto_service_annotations_java':
                 sb.append('  preferred_dep = true\n')
                 break
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
index 03d0303..3a2441ff 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -28,9 +28,6 @@
             url: 'https://maven.google.com/androidx/multidex/multidex/2.0.0/multidex-2.0.0.aar'),
         com_google_android_datatransport_transport_api: new PropertyOverride(
             description: 'Interfaces for data logging in GmsCore SDKs.'),
-        // Chrome uses the window APIs directly instead of going through the androidx middleware.
-        // See //third_party/android_sdk/window_extensions/README.md
-        androidx_window_window: new PropertyOverride(exclude: true),
         com_google_android_datatransport_transport_backend_cct: new PropertyOverride(
             exclude: true),  // We're not using datatransport functionality.
         com_google_android_datatransport_transport_runtime: new PropertyOverride(
@@ -41,12 +38,6 @@
             description: 'Base library for gmscore / Google Play Services.'),
         com_google_android_gms_play_services_location: new PropertyOverride(
             description: 'Provides data about the device\'s physical location via gmscore.'),
-        com_google_auto_auto_common: new PropertyOverride(
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
-        com_google_auto_service_auto_service: new PropertyOverride(
-            licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
         com_google_auto_service_auto_service_annotations: new PropertyOverride(
             licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
             licenseName: 'Apache 2.0'),
@@ -252,11 +243,14 @@
             licenseName: 'MIT'),
         // Prevent version changing ~weekly. https://crbug.com/1257197
         org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm: new PropertyOverride(
-            resolveVersion: '1.6.4'),
+            resolveVersion: '1.8.1'),
         org_jetbrains_kotlinx_kotlinx_coroutines_android: new PropertyOverride(
-            resolveVersion: '1.6.4'),
+            resolveVersion: '1.8.1'),
         org_jetbrains_kotlinx_kotlinx_coroutines_guava: new PropertyOverride(
-            resolveVersion: '1.6.4'),
+            resolveVersion: '1.8.1'),
+        org_jetbrains_kotlinx_kotlinx_serialization_core_jvm: new PropertyOverride(
+            resolveVersion: '1.7.2',
+            overrideLatest: true),
         io_grpc_grpc_binder: new PropertyOverride(
             licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
             licenseName: 'Apache 2.0'),
diff --git a/third_party/android_deps/libs/com_google_auto_auto_common/LICENSE b/third_party/android_deps/libs/com_google_auto_auto_common/LICENSE
deleted file mode 100644
index d645695..0000000
--- a/third_party/android_deps/libs/com_google_auto_auto_common/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   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.
diff --git a/third_party/android_deps/libs/com_google_auto_auto_common/README.chromium b/third_party/android_deps/libs/com_google_auto_auto_common/README.chromium
deleted file mode 100644
index cfb064c76..0000000
--- a/third_party/android_deps/libs/com_google_auto_auto_common/README.chromium
+++ /dev/null
@@ -1,15 +0,0 @@
-Name: Auto Common Libraries
-Short Name: auto-common
-URL: https://github.com/google/auto/tree/master/common
-Version: 1.2.1
-License: Apache 2.0
-License File: LICENSE
-CPEPrefix: unknown
-Security Critical: no
-Shipped: no
-
-Description:
-Common utilities for creating annotation processors.
-
-Local Modifications:
-No modifications.
diff --git a/third_party/android_deps/libs/com_google_auto_auto_common/cipd.yaml b/third_party/android_deps/libs/com_google_auto_auto_common/cipd.yaml
deleted file mode 100644
index 5ffa785..0000000
--- a/third_party/android_deps/libs/com_google_auto_auto_common/cipd.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright 2018 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.2.1.cr1
-package: chromium/third_party/android_deps/libs/com_google_auto_auto_common
-description: "Auto Common Libraries"
-data:
-- file: auto-common-1.2.1.jar
diff --git a/third_party/android_deps/libs/com_google_auto_service_auto_service/3pp/3pp.pb b/third_party/android_deps/libs/com_google_auto_service_auto_service/3pp/3pp.pb
deleted file mode 100644
index d743b51b..0000000
--- a/third_party/android_deps/libs/com_google_auto_service_auto_service/3pp/3pp.pb
+++ /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.
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-
-create {
-  source {
-    script { name: "fetch.py" }
-  }
-}
-
-upload {
-  pkg_prefix: "chromium/third_party/android_deps/libs"
-  universal: true
-}
diff --git a/third_party/android_deps/libs/com_google_auto_service_auto_service/3pp/fetch.py b/third_party/android_deps/libs/com_google_auto_service_auto_service/3pp/fetch.py
deleted file mode 100755
index bfe2fd4..0000000
--- a/third_party/android_deps/libs/com_google_auto_service_auto_service/3pp/fetch.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python3
-# 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.
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy and
-# 3ppFetch.template instead.
-
-import pathlib
-import sys
-
-_3PP_DIR = pathlib.Path(__file__).resolve().parent
-sys.path.insert(0, str(_3PP_DIR.parents[2]))
-import fetch_common
-
-_REPO_URL = 'https://repo.maven.apache.org/maven2'
-SPEC = fetch_common.Spec(repo_url=_REPO_URL,
-                         group_name='com/google/auto/service',
-                         module_name='auto-service',
-                         file_ext='jar',
-                         patch_version='cr1',
-                         version_override=None,
-                         version_filter=None)
-
-if __name__ == '__main__':
-    fetch_common.main(SPEC)
diff --git a/third_party/android_deps/libs/com_google_auto_service_auto_service/LICENSE b/third_party/android_deps/libs/com_google_auto_service_auto_service/LICENSE
deleted file mode 100644
index d645695..0000000
--- a/third_party/android_deps/libs/com_google_auto_service_auto_service/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   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.
diff --git a/third_party/android_deps/libs/com_google_auto_service_auto_service/OWNERS b/third_party/android_deps/libs/com_google_auto_service_auto_service/OWNERS
deleted file mode 100644
index aea47a05..0000000
--- a/third_party/android_deps/libs/com_google_auto_service_auto_service/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://third_party/android_deps/OWNERS
diff --git a/third_party/android_deps/libs/com_google_auto_service_auto_service/README.chromium b/third_party/android_deps/libs/com_google_auto_service_auto_service/README.chromium
deleted file mode 100644
index d336fad..0000000
--- a/third_party/android_deps/libs/com_google_auto_service_auto_service/README.chromium
+++ /dev/null
@@ -1,15 +0,0 @@
-Name: AutoService Processor
-Short Name: auto-service
-URL: https://github.com/google/auto/tree/master/service
-Version: 1.0-rc6
-License: Apache 2.0
-License File: LICENSE
-CPEPrefix: unknown
-Security Critical: no
-Shipped: no
-
-Description:
-Provider-configuration files for ServiceLoader.
-
-Local Modifications:
-No modifications.
diff --git a/third_party/android_deps/libs/com_google_auto_service_auto_service/cipd.yaml b/third_party/android_deps/libs/com_google_auto_service_auto_service/cipd.yaml
deleted file mode 100644
index 84bdf478..0000000
--- a/third_party/android_deps/libs/com_google_auto_service_auto_service/cipd.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright 2018 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@1.0-rc6.cr1
-package: chromium/third_party/android_deps/libs/com_google_auto_service_auto_service
-description: "AutoService Processor"
-data:
-- file: auto-service-1.0-rc6.jar
diff --git a/third_party/android_deps/libs/com_google_auto_auto_common/3pp/3pp.pb b/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/3pp/3pp.pb
similarity index 100%
rename from third_party/android_deps/libs/com_google_auto_auto_common/3pp/3pp.pb
rename to third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/3pp/3pp.pb
diff --git a/third_party/android_deps/libs/com_google_auto_auto_common/3pp/fetch.py b/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/3pp/fetch.py
similarity index 78%
rename from third_party/android_deps/libs/com_google_auto_auto_common/3pp/fetch.py
rename to third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/3pp/fetch.py
index bc538c4..3caf56e 100755
--- a/third_party/android_deps/libs/com_google_auto_auto_common/3pp/fetch.py
+++ b/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/3pp/fetch.py
@@ -15,11 +15,11 @@
 
 _REPO_URL = 'https://repo.maven.apache.org/maven2'
 SPEC = fetch_common.Spec(repo_url=_REPO_URL,
-                         group_name='com/google/auto',
-                         module_name='auto-common',
+                         group_name='org/jetbrains/kotlinx',
+                         module_name='kotlinx-serialization-core-jvm',
                          file_ext='jar',
                          patch_version='cr1',
-                         version_override=None,
+                         version_override='1.7.2',
                          version_filter=None)
 
 if __name__ == '__main__':
diff --git a/third_party/android_deps/libs/com_google_auto_auto_common/OWNERS b/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/OWNERS
similarity index 100%
rename from third_party/android_deps/libs/com_google_auto_auto_common/OWNERS
rename to third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/OWNERS
diff --git a/third_party/angle b/third_party/angle
index 11c0a783..f0919be 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 11c0a783110452d90c45f65c79196c1b0d096e6b
+Subproject commit f0919be383d0d6ad76120fd7a3ed6b3beddb74d7
diff --git a/third_party/blink/public/common/widget/visual_properties.h b/third_party/blink/public/common/widget/visual_properties.h
index 5cd37e2b..ed289864 100644
--- a/third_party/blink/public/common/widget/visual_properties.h
+++ b/third_party/blink/public/common/widget/visual_properties.h
@@ -111,8 +111,7 @@
   mojom::DisplayMode display_mode = mojom::DisplayMode::kUndefined;
 
   // The window show state. Defaults to `SHOW_STATE_DEFAULT`.
-  ui::WindowShowState window_show_state =
-      ui::WindowShowState::SHOW_STATE_DEFAULT;
+  ui::WindowShowState window_show_state = ui::SHOW_STATE_DEFAULT;
 
   // This represents the latest capture sequence number requested. When this is
   // incremented, that means the caller wants to synchronize surfaces which
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 8c29637..6f07fe3 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -2679,6 +2679,7 @@
       after
       marker
       backdrop
+      column
       selection
       search-text
       target-text
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index cf6286d..370568d 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -324,6 +324,7 @@
     "//ui/base/dragdrop/mojom",
     "//ui/base/ime/mojom",
     "//ui/base/mojom",
+    "//ui/base/mojom:ui_base_types",
     "//ui/display/mojom",
     "//ui/events/mojom",
     "//ui/events/mojom:event_latency_metadata_mojom",
diff --git a/third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom b/third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom
index 234f46e..18685aaa 100644
--- a/third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom
+++ b/third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom
@@ -40,6 +40,8 @@
   // Controls whether or not to include javascript stack traces in crash reports
   // from the Reporting API.
   kIncludeJSCallStacksInCrashReports = 14,
+  // Hints if the document is not expecting embedded resources.
+  kExpectNoEmbeddedResources = 15,
 
   // Don't change assigned numbers of any item, and don't reuse removed slots.
   // Add new features at the end of the enum.
diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
index bcf7f20..3fe9937 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
@@ -864,6 +864,7 @@
     kTextWrapMode = 805,
     kTextWrapStyle = 806,
     kMasonryTrack = 807,
+    kMasonrySlack = 808,
 
     // 1. Add new features above this line (don't change the assigned numbers of
     //    the existing items).
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 f0a581e..a6cca367 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
@@ -4472,6 +4472,8 @@
   kV8Window_PopinContextType_Method = 5097,
   kWebAuthentication_AttestationFormats = 5098,
   kCssDisplayPropertyMultipleValues = 5099,
+  kDocumentPolicyExpectNoEmbeddedResources = 5100,
+
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots. Also don't add extra
diff --git a/third_party/blink/public/mojom/widget/visual_properties.mojom b/third_party/blink/public/mojom/widget/visual_properties.mojom
index 47dfbdf..b71e802 100644
--- a/third_party/blink/public/mojom/widget/visual_properties.mojom
+++ b/third_party/blink/public/mojom/widget/visual_properties.mojom
@@ -9,7 +9,7 @@
 import "third_party/blink/public/mojom/manifest/display_mode.mojom";
 import "ui/display/mojom/screen_infos.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
-import "ui/base/mojom/ui_base_types.mojom";
+import "ui/base/mojom/window_show_state.mojom";
 
 // See public/common/widget/visual_properties.h
 struct VisualProperties {
@@ -71,7 +71,7 @@
 
   // The window show state.
   ui.mojom.WindowShowState window_show_state
-      = ui.mojom.WindowShowState.SHOW_STATE_DEFAULT;
+      = ui.mojom.WindowShowState.kDefault;
 
   // This represents the latest capture sequence number requested. When this is
   // incremented, that means the caller wants to synchronize surfaces which
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni
index 9412eeb..0ad14d4 100644
--- a/third_party/blink/renderer/bindings/bindings.gni
+++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -120,7 +120,6 @@
                     "core/v8/serialization/transferables.h",
                     "core/v8/serialization/unpacked_serialized_script_value.cc",
                     "core/v8/serialization/unpacked_serialized_script_value.h",
-                    "core/v8/serialization/v8_external_memory_accounter.h",
                     "core/v8/serialization/v8_script_value_deserializer.cc",
                     "core/v8/serialization/v8_script_value_deserializer.h",
                     "core/v8/serialization/v8_script_value_serializer.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/async_iterable.cc b/third_party/blink/renderer/bindings/core/v8/async_iterable.cc
index e4e5695069..90352c558 100644
--- a/third_party/blink/renderer/bindings/core/v8/async_iterable.cc
+++ b/third_party/blink/renderer/bindings/core/v8/async_iterable.cc
@@ -37,7 +37,9 @@
       : AsyncIterationSourceBase::CallableCommon(iteration_source) {}
 
   ScriptValue Call(ScriptState* script_state, ScriptValue) override {
-    return iteration_source_->RunNextSteps(script_state).AsScriptValue();
+    return ScriptValue(
+        script_state->GetIsolate(),
+        iteration_source_->RunNextSteps(script_state).V8Promise());
   }
 };
 
@@ -74,8 +76,9 @@
         value_(std::move(value)) {}
 
   ScriptValue Call(ScriptState* script_state, ScriptValue) override {
-    return iteration_source_->RunReturnSteps(script_state, value_)
-        .AsScriptValue();
+    return ScriptValue(
+        script_state->GetIsolate(),
+        iteration_source_->RunReturnSteps(script_state, value_).V8Promise());
   }
 
   void Trace(Visitor* visitor) const override {
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record_test.cc b/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
index 69d099d..b4ca309 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
@@ -150,16 +150,16 @@
   EXPECT_EQ(0u, requests[1].import_attributes.size());
 }
 
-TEST_F(ModuleRecordTest, moduleRequestsWithImportAssertions) {
+TEST_F(ModuleRecordTest, moduleRequestsWithImportAttributes) {
   V8TestingScope scope;
-  v8::V8::SetFlagsFromString("--harmony-import-assertions");
+  v8::V8::SetFlagsFromString("--harmony-import-attributes");
   const KURL js_url("https://example.com/foo.js");
-  v8::Local<v8::Module> module = ModuleTestBase::CompileModule(
-      scope.GetScriptState(),
-      "import 'a' assert { };"
-      "import 'b' assert { type: 'x'};"
-      "import 'c' assert { foo: 'y', type: 'z' };",
-      js_url);
+  v8::Local<v8::Module> module =
+      ModuleTestBase::CompileModule(scope.GetScriptState(),
+                                    "import 'a' with { };"
+                                    "import 'b' with { type: 'x'};"
+                                    "import 'c' with { foo: 'y', type: 'z' };",
+                                    js_url);
   ASSERT_FALSE(module.IsEmpty());
 
   auto requests = ModuleRecord::ModuleRequests(scope.GetScriptState(), module);
diff --git a/third_party/blink/renderer/bindings/core/v8/script_iterator.cc b/third_party/blink/renderer/bindings/core/v8/script_iterator.cc
index 5d3d6cc6..1165d161 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_iterator.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_iterator.cc
@@ -140,8 +140,7 @@
 }
 
 bool ScriptIterator::Next(ExecutionContext* execution_context,
-                          ExceptionState& exception_state,
-                          v8::Local<v8::Value> value) {
+                          ExceptionState& exception_state) {
   DCHECK(!IsNull());
 
   ScriptState* script_state = ScriptState::ForCurrentRealm(isolate_);
@@ -154,10 +153,9 @@
 
   TryRethrowScope rethrow_scope(isolate_, exception_state);
   v8::Local<v8::Value> next_return_value;
-  if (!V8ScriptRunner::CallFunction(next_method.As<v8::Function>(),
-                                    execution_context,
-                                    iterator_.Get(script_state),
-                                    value.IsEmpty() ? 0 : 1, &value, isolate_)
+  if (!V8ScriptRunner::CallFunction(
+           next_method.As<v8::Function>(), execution_context,
+           iterator_.Get(script_state), 0, nullptr, isolate_)
            .ToLocal(&next_return_value)) {
     done_ = true;
     return false;
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 a1ea1b8..3aba5b4 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_iterator.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_iterator.h
@@ -160,8 +160,7 @@
 
   // Returns true if the iterator is still not done.
   bool Next(ExecutionContext* execution_context,
-            ExceptionState& exception_state,
-            v8::Local<v8::Value> value = v8::Local<v8::Value>());
+            ExceptionState& exception_state);
 
   v8::MaybeLocal<v8::Value> GetValue() {
     return value_.Get(ScriptState::ForCurrentRealm(isolate_));
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 c68ecec..31eaab4 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise.h
@@ -81,8 +81,6 @@
   ScriptPromise<IDLAny> Then(ScriptFunction* on_fulfilled,
                              ScriptFunction* on_rejected = nullptr);
 
-  ScriptValue AsScriptValue() const { return promise_; }
-
   v8::Local<v8::Value> V8Value() const { return promise_.V8Value(); }
   v8::Local<v8::Promise> V8Promise() const {
     // This is safe because `promise_` always stores a promise value as long
@@ -241,6 +239,13 @@
   static constexpr bool kCanClearUnusedSlotsWithMemset = true;
 };
 
+template <typename T>
+struct VectorTraits<blink::ScriptPromise<T>>
+    : VectorTraitsBase<blink::ScriptPromise<T>> {
+  STATIC_ONLY(VectorTraits);
+  static constexpr bool kCanClearUnusedSlotsWithMemset = true;
+};
+
 }  // namespace WTF
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_PROMISE_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_external_memory_accounter.h b/third_party/blink/renderer/bindings/core/v8/serialization/v8_external_memory_accounter.h
deleted file mode 100644
index ffd4b16..0000000
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_external_memory_accounter.h
+++ /dev/null
@@ -1,40 +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 THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SERIALIZATION_V8_EXTERNAL_MEMORY_ACCOUNTER_H_
-#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SERIALIZATION_V8_EXTERNAL_MEMORY_ACCOUNTER_H_
-
-#include <stdlib.h>
-
-#include "base/check_op.h"
-#include "v8/include/v8-isolate.h"
-
-namespace blink {
-
-class V8ExternalMemoryAccounter {
- public:
-  ~V8ExternalMemoryAccounter() { Unregister(); }
-
-  void Register(size_t size) {
-    CHECK_EQ(amount_of_external_memory_, 0u);
-    v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
-        static_cast<int64_t>(size));
-    amount_of_external_memory_ = size;
-  }
-
-  void Unregister() {
-    if (amount_of_external_memory_ > 0) {
-      v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
-          -static_cast<int64_t>(amount_of_external_memory_));
-      amount_of_external_memory_ = 0;
-    }
-  }
-
- private:
-  size_t amount_of_external_memory_ = 0;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SERIALIZATION_V8_EXTERNAL_MEMORY_ACCOUNTER_H_
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 4025b092..d2551289 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -1022,7 +1022,7 @@
     return node
 
 
-def make_check_coop_restrict_properties_access(cg_context):
+def make_check_proxy_access(cg_context):
     assert isinstance(cg_context, CodeGenContext)
 
     T = TextNode
@@ -1034,8 +1034,9 @@
     if "CrossOrigin" not in ext_attrs:
         return None
 
-    # COOP: restrict-properties never restricts postMessage() and closed
-    # accesses, which should still be possible across browsing context groups.
+    # COOP: restrict-properties and Partitioned Popins never restrict
+    # postMessage() and closed accesses, which should still be possible across
+    # browsing context groups.
     if cg_context.property_.identifier in ("postMessage", "closed"):
         return None
 
@@ -1051,15 +1052,13 @@
         error_exit_return_statement = "return;"
 
     return CxxUnlikelyIfNode(
-        cond=("${blink_receiver}->"
-              "IsAccessBlockedByCoopRestrictProperties(${isolate})"),
+        cond=
+        ("auto reason = ${blink_receiver}->GetProxyAccessBlockedReason(${isolate})"
+         ),
         attribute="[[unlikely]]",
         body=[
-            T("""\
-${exception_state}.ThrowSecurityError(
-"Cross-Origin-Opener-Policy: 'restrict-properties' blocked the access.",
-"Cross-Origin-Opener-Policy: 'restrict-properties' blocked the access.");\
-"""),
+            T("${exception_state}.ThrowSecurityError("
+              "DOMWindow::GetProxyAccessBlockedExceptionMessage(*reason));"),
             T(error_exit_return_statement),
         ])
 
@@ -2044,7 +2043,7 @@
         make_report_measure_as(cg_context),
         make_log_activity(cg_context),
         EmptyNode(),
-        make_check_coop_restrict_properties_access(cg_context),
+        make_check_proxy_access(cg_context),
         EmptyNode(),
         make_return_value_cache_return_early(cg_context),
         EmptyNode(),
@@ -2722,7 +2721,7 @@
         make_report_measure_as(cg_context),
         make_log_activity(cg_context),
         EmptyNode(),
-        make_check_coop_restrict_properties_access(cg_context),
+        make_check_proxy_access(cg_context),
         EmptyNode(),
         make_check_argument_length(cg_context),
         EmptyNode(),
@@ -3923,7 +3922,7 @@
     # Do this before the index verification below, because we do not want to
     # reveal any information about the number of frames in this window.
     body.extend([
-        make_check_coop_restrict_properties_access(cg_context),
+        make_check_proxy_access(cg_context),
         EmptyNode(),
     ])
 
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 1afe5ec..49caad9 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -3784,6 +3784,21 @@
       invalidate: ["paint"],
     },
     {
+      // TODO(crbug.com/343257585): We might need to alter the spec's grammar to accept
+      // normal | <length-percentage>. With normal, we use `1em` for slack size.
+      name: "masonry-slack",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
+      field_group: "*",
+      field_template: "external",
+      default_value: "std::nullopt",
+      type_name: "std::optional<Length>",
+      converter: "ConvertMasonrySlack",
+      typedom_types: ["Length", "Percentage"],
+      keywords: ["normal"],
+      invalidate: ["layout", "paint"],
+      runtime_flag: "CSSMasonryLayout",
+    },
+    {
       name: "masonry-template-tracks",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
       layout_dependent: true,
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc
index 74f2ff5..a35c559 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -516,6 +516,8 @@
       return a.MarkerStartResource() == b.MarkerStartResource();
     case CSSPropertyID::kMaskType:
       return a.MaskType() == b.MaskType();
+    case CSSPropertyID::kMasonrySlack:
+      return a.MasonrySlack() == b.MasonrySlack();
     case CSSPropertyID::kMasonryTemplateTracks:
       return a.MasonryTemplateTracks() == b.MasonryTemplateTracks();
     case CSSPropertyID::kMasonryTrackEnd:
diff --git a/third_party/blink/renderer/core/css/css_selector.cc b/third_party/blink/renderer/core/css/css_selector.cc
index d1822c0..1bc060fa 100644
--- a/third_party/blink/renderer/core/css/css_selector.cc
+++ b/third_party/blink/renderer/core/css/css_selector.cc
@@ -336,6 +336,8 @@
       return kPseudoIdScrollNextButton;
     case kPseudoScrollPrevButton:
       return kPseudoIdScrollPrevButton;
+    case kPseudoColumn:
+      return kPseudoIdColumn;
     case kPseudoScrollbarButton:
       return kPseudoIdScrollbarButton;
     case kPseudoScrollbarCorner:
@@ -559,6 +561,7 @@
     {"before", CSSSelector::kPseudoBefore},
     {"checked", CSSSelector::kPseudoChecked},
     {"closed", CSSSelector::kPseudoClosed},
+    {"column", CSSSelector::kPseudoColumn},
     {"corner-present", CSSSelector::kPseudoCornerPresent},
     {"cue", CSSSelector::kPseudoWebKitCustomElement},
     {"current", CSSSelector::kPseudoCurrent},
@@ -740,6 +743,11 @@
     return CSSSelector::kPseudoUnknown;
   }
 
+  if (match->type == CSSSelector::kPseudoColumn &&
+      !RuntimeEnabledFeatures::CSSPseudoColumnEnabled()) {
+    return CSSSelector::kPseudoUnknown;
+  }
+
   if ((match->type == CSSSelector::kPseudoOpen ||
        match->type == CSSSelector::kPseudoClosed) &&
       !RuntimeEnabledFeatures::CSSPseudoOpenClosedEnabled()) {
@@ -850,6 +858,7 @@
     case kPseudoScrollMarkerGroup:
     case kPseudoScrollNextButton:
     case kPseudoScrollPrevButton:
+    case kPseudoColumn:
     case kPseudoSelectFallbackButton:
     case kPseudoSelectFallbackButtonText:
     case kPseudoPicker:
@@ -1589,6 +1598,7 @@
     case kPseudoScrollMarker:
     case kPseudoScrollMarkerGroup:
     case kPseudoScrollNextButton:
+    case kPseudoColumn:
     case kPseudoScrollPrevButton:
     case kPseudoWebKitCustomElement:
     case kPseudoBlinkInternalElement:
diff --git a/third_party/blink/renderer/core/css/css_selector.h b/third_party/blink/renderer/core/css/css_selector.h
index ea4f0849..8793711 100644
--- a/third_party/blink/renderer/core/css/css_selector.h
+++ b/third_party/blink/renderer/core/css/css_selector.h
@@ -356,6 +356,8 @@
     // Pseudo elements in UA ShadowRoots. Available only in UA stylesheets.
     kPseudoBlinkInternalElement,
     kPseudoClosed,
+    // Pseudo element for fragment styling
+    kPseudoColumn,
     kPseudoCue,
     kPseudoDefined,
     kPseudoDir,
diff --git a/third_party/blink/renderer/core/css/invalidation/rule_invalidation_data_visitor.cc b/third_party/blink/renderer/core/css/invalidation/rule_invalidation_data_visitor.cc
index 2d86ee8..6b61096 100644
--- a/third_party/blink/renderer/core/css/invalidation/rule_invalidation_data_visitor.cc
+++ b/third_party/blink/renderer/core/css/invalidation/rule_invalidation_data_visitor.cc
@@ -111,6 +111,7 @@
     case CSSSelector::kPseudoScrollMarker:
     case CSSSelector::kPseudoScrollNextButton:
     case CSSSelector::kPseudoScrollPrevButton:
+    case CSSSelector::kPseudoColumn:
     case CSSSelector::kPseudoWindowInactive:
     case CSSSelector::kPseudoSelection:
     case CSSSelector::kPseudoCornerPresent:
diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc
index 119a1be..fdfac37 100644
--- a/third_party/blink/renderer/core/css/media_values.cc
+++ b/third_party/blink/renderer/core/css/media_values.cc
@@ -315,7 +315,7 @@
       frame->GetPage()->GetSettings().GetWindowShowState();
   // Initial state set in /third_party/blink/renderer/core/frame/settings.json5
   // should match with this.
-  if (show_state != ui::WindowShowState::SHOW_STATE_DEFAULT) {
+  if (show_state != ui::SHOW_STATE_DEFAULT) {
     return show_state;
   }
 
diff --git a/third_party/blink/renderer/core/css/media_values_cached.h b/third_party/blink/renderer/core/css/media_values_cached.h
index 1b27eabf..273aa1e9 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.h
+++ b/third_party/blink/renderer/core/css/media_values_cached.h
@@ -68,8 +68,7 @@
     String media_type;
     mojom::blink::DisplayMode display_mode =
         mojom::blink::DisplayMode::kBrowser;
-    ui::WindowShowState window_show_state =
-        ui::WindowShowState::SHOW_STATE_DEFAULT;
+    ui::WindowShowState window_show_state = ui::SHOW_STATE_DEFAULT;
     bool resizable = true;
     ColorSpaceGamut color_gamut = ColorSpaceGamut::kUnknown;
     mojom::blink::PreferredColorScheme preferred_color_scheme =
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
index ffb70a7c..a6c2c2f5 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
@@ -510,6 +510,52 @@
   }
 }
 
+TEST(CSSSelectorParserTest, ColumnPseudo) {
+  test::TaskEnvironment task_environment;
+  struct TestCase {
+    const char* selector;
+    CSSSelector::PseudoType type;
+  };
+
+  TestCase test_cases[] = {
+      {".scroller::column", CSSSelector::kPseudoColumn},
+      {"#scroller::column", CSSSelector::kPseudoColumn},
+      {"div::column", CSSSelector::kPseudoColumn},
+      {"div::before::column", CSSSelector::kPseudoUnknown},
+      {"div::after::column", CSSSelector::kPseudoUnknown},
+  };
+
+  HeapVector<CSSSelector> arena;
+  for (const auto& test_case : test_cases) {
+    SCOPED_TRACE(test_case.selector);
+    CSSParserTokenStream stream(StringView(test_case.selector));
+    base::span<CSSSelector> vector = CSSSelectorParser::ParseSelector(
+        stream,
+        MakeGarbageCollected<CSSParserContext>(
+            kHTMLStandardMode, SecureContextMode::kInsecureContext),
+        CSSNestingType::kNone, /*parent_rule_for_nesting=*/nullptr,
+        /*is_within_scope=*/false,
+        /*semicolon_aborts_nested_selector=*/false, nullptr, arena);
+
+    if (test_case.type == CSSSelector::kPseudoUnknown) {
+      EXPECT_TRUE(vector.empty());
+      return;
+    }
+
+    EXPECT_TRUE(!vector.empty());
+
+    CSSSelectorList* list = CSSSelectorList::AdoptSelectorVector(vector);
+    ASSERT_TRUE(list->HasOneSelector());
+
+    const CSSSelector* selector = list->First();
+    while (selector->NextSimpleSelector()) {
+      selector = selector->NextSimpleSelector();
+    }
+
+    EXPECT_EQ(selector->GetPseudoType(), test_case.type);
+  }
+}
+
 // Pseudo-elements are not valid within :is() as per the spec:
 // https://drafts.csswg.org/selectors-4/#matches
 static const SelectorTestCase invalid_pseudo_is_argments_data[] = {
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 849739b..3bc5459b 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -2218,6 +2218,13 @@
   return list;
 }
 
+CSSValue* ComputedStyleUtils::ValueForMasonrySlack(
+    const std::optional<Length>& slack_length,
+    const ComputedStyle& style) {
+  return slack_length ? ZoomAdjustedPixelValueForLength(*slack_length, style)
+                      : CSSIdentifierValue::Create(CSSValueID::kNormal);
+}
+
 CSSValue* ComputedStyleUtils::ValueForMasonryTrackList(
     const LayoutObject* layout_object,
     const ComputedStyle& style) {
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.h b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
index c45097c..76d7d27 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.h
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
@@ -159,6 +159,8 @@
                                          const ComputedStyle&,
                                          bool force_computed_value = false);
   static CSSValue* ValueForGridPosition(const GridPosition&);
+  static CSSValue* ValueForMasonrySlack(const std::optional<Length>&,
+                                        const ComputedStyle&);
   static CSSValue* ValueForMasonryTrackList(const LayoutObject*,
                                             const ComputedStyle&);
   static gfx::SizeF UsedBoxSize(const LayoutObject&);
diff --git a/third_party/blink/renderer/core/css/properties/css_color_function_parser.cc b/third_party/blink/renderer/core/css/properties/css_color_function_parser.cc
index 10c4f956..85d53146 100644
--- a/third_party/blink/renderer/core/css/properties/css_color_function_parser.cc
+++ b/third_party/blink/renderer/core/css/properties/css_color_function_parser.cc
@@ -142,6 +142,21 @@
                                   CSSToLengthConversionData());
     }
   }
+  if (auto* relative_color_value =
+          DynamicTo<cssvalue::CSSRelativeColorValue>(value)) {
+    auto origin_color =
+        TryResolveAtParseTime(relative_color_value->OriginColor());
+    if (!origin_color) {
+      return std::nullopt;
+    }
+    StyleColor::UnresolvedRelativeColor* unresolved_relative_color =
+        MakeGarbageCollected<StyleColor::UnresolvedRelativeColor>(
+            StyleColor(origin_color.value()),
+            relative_color_value->ColorInterpolationSpace(),
+            relative_color_value->Channel0(), relative_color_value->Channel1(),
+            relative_color_value->Channel2(), relative_color_value->Alpha());
+    return unresolved_relative_color->Resolve(Color());
+  }
   return std::nullopt;
 }
 
@@ -235,7 +250,8 @@
 
   if (unresolved_origin_color_) {
     origin_color_ = TryResolveAtParseTime(*unresolved_origin_color_);
-    if (origin_color_.has_value()) {
+    if (origin_color_.has_value() &&
+        !RuntimeEnabledFeatures::CSSRelativeColorLateResolveAlwaysEnabled()) {
       origin_color_->ConvertToColorSpace(color_space_);
       // Relative color syntax requires "channel keyword" substitutions for
       // color channels. Each color space has three "channel keywords", plus
@@ -265,7 +281,8 @@
           {CSSValueID::kAlpha, origin_color_->Alpha()},
       };
     } else {
-      if (!RuntimeEnabledFeatures::
+      if (!origin_color_.has_value() &&
+          !RuntimeEnabledFeatures::
               CSSRelativeColorSupportsCurrentcolorEnabled()) {
         return false;
       }
@@ -616,10 +633,14 @@
   }
   stream.ConsumeWhitespace();
 
-  // We should be able to resolve channel values for all non-relative colors
-  // and all relative colors where the origin color is resolvable at parse
-  // time.
-  if (!IsRelativeColor() || origin_color_.has_value()) {
+  // For non-relative colors, resolve channel values at parse time.
+  // For relative colors:
+  // - (Legacy behavior) Resolve channel values at parse time if the origin
+  //   color is resolvable at parse time.
+  // - (WPT-compliant behavior) Always defer resolution until used-value time.
+  if (!IsRelativeColor() ||
+      (origin_color_.has_value() &&
+       !RuntimeEnabledFeatures::CSSRelativeColorLateResolveAlwaysEnabled())) {
     // Resolve channel values.
     for (int i = 0; i < 3; i++) {
       if (channel_types_[i] != ChannelType::kNone) {
diff --git a/third_party/blink/renderer/core/css/properties/css_color_function_parser_test.cc b/third_party/blink/renderer/core/css/properties/css_color_function_parser_test.cc
index 6e0d9567..7715cba0 100644
--- a/third_party/blink/renderer/core/css/properties/css_color_function_parser_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_color_function_parser_test.cc
@@ -15,7 +15,47 @@
 
 namespace blink {
 
-TEST(ColorFunctionParserTest, RelativeColorWithKeywordBase) {
+TEST(ColorFunctionParserTest, RelativeColorWithKeywordBase_LateResolveEnabled) {
+  ScopedCSSRelativeColorLateResolveAlwaysForTest scoped_feature_for_test(true);
+
+  const String test_case = "rgb(from red r g b)";
+  CSSParserTokenStream stream(test_case);
+
+  const CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
+      kHTMLStandardMode, SecureContextMode::kInsecureContext);
+
+  ColorFunctionParser parser;
+  const CSSValue* result =
+      parser.ConsumeFunctionalSyntaxColor(stream, *context);
+  EXPECT_TRUE(result->IsRelativeColorValue());
+  const cssvalue::CSSRelativeColorValue* color =
+      To<cssvalue::CSSRelativeColorValue>(result);
+
+  const CSSValue& origin = color->OriginColor();
+  EXPECT_TRUE(origin.IsIdentifierValue());
+  EXPECT_EQ(To<CSSIdentifierValue>(origin).GetValueID(), CSSValueID::kRed);
+
+  EXPECT_EQ(color->ColorInterpolationSpace(), Color::ColorSpace::kSRGBLegacy);
+
+  const CSSValue& channel0 = color->Channel0();
+  EXPECT_TRUE(channel0.IsIdentifierValue());
+  EXPECT_EQ(To<CSSIdentifierValue>(channel0).GetValueID(), CSSValueID::kR);
+
+  const CSSValue& channel1 = color->Channel1();
+  EXPECT_TRUE(channel1.IsIdentifierValue());
+  EXPECT_EQ(To<CSSIdentifierValue>(channel1).GetValueID(), CSSValueID::kG);
+
+  const CSSValue& channel2 = color->Channel2();
+  EXPECT_TRUE(channel2.IsIdentifierValue());
+  EXPECT_EQ(To<CSSIdentifierValue>(channel2).GetValueID(), CSSValueID::kB);
+
+  EXPECT_EQ(color->Alpha(), nullptr);
+}
+
+TEST(ColorFunctionParserTest,
+     RelativeColorWithKeywordBase_LateResolveDisabled) {
+  ScopedCSSRelativeColorLateResolveAlwaysForTest scoped_feature_for_test(false);
+
   const String test_case = "rgb(from red r g b)";
   CSSParserTokenStream stream(test_case);
 
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 26619a5..d3f0b5f7 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -6401,6 +6401,15 @@
   return false;
 }
 
+CSSValue* ConsumeMasonrySlack(CSSParserTokenStream& stream,
+                              const CSSParserContext& context) {
+  if (stream.Peek().Id() == CSSValueID::kNormal) {
+    return ConsumeIdent(stream);
+  }
+  return ConsumeLengthOrPercent(stream, context,
+                                CSSPrimitiveValue::ValueRange::kNonNegative);
+}
+
 CSSValue* ConsumeHyphenateLimitChars(CSSParserTokenStream& stream,
                                      const CSSParserContext& context) {
   CSSValueList* const list = CSSValueList::CreateSpaceSeparated();
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
index 55dd9964..b609135 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
@@ -489,6 +489,8 @@
                                   const CSSValue*& template_columns,
                                   const CSSValue*& template_areas);
 
+CSSValue* ConsumeMasonrySlack(CSSParserTokenStream&, const CSSParserContext&);
+
 CSSValue* ConsumeHyphenateLimitChars(CSSParserTokenStream&,
                                      const CSSParserContext&);
 
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 2f0b4e9..bceab7a 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -5986,6 +5986,21 @@
   return CSSIdentifierValue::Create(style.MaskType());
 }
 
+const CSSValue* MasonrySlack::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    const CSSParserLocalContext&) const {
+  return css_parsing_utils::ConsumeMasonrySlack(stream, context);
+}
+
+const CSSValue* MasonrySlack::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  return ComputedStyleUtils::ValueForMasonrySlack(style.MasonrySlack(), style);
+}
+
 const CSSValue* MasonryTemplateTracks::ParseSingleValue(
     CSSParserTokenStream& stream,
     const CSSParserContext& context,
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index b853960..a91993870 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -86,6 +86,7 @@
 #include "third_party/blink/renderer/core/layout/list/list_marker.h"
 #include "third_party/blink/renderer/core/mathml/mathml_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/style/computed_style_base_constants.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
 #include "third_party/blink/renderer/core/style/style_intrinsic_length.h"
 #include "third_party/blink/renderer/core/svg/svg_foreign_object_element.h"
@@ -99,6 +100,7 @@
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/transforms/transform_operations.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "ui/base/ui_base_features.h"
 
 namespace blink {
@@ -626,12 +628,28 @@
   }
 }
 
+// g-issues.chromium.org/issues/349835587
+// https://github.com/WICG/canvas-place-element
+static bool IsCanvasPlacedElement(const Element* element) {
+  if (RuntimeEnabledFeatures::CanvasPlaceElementEnabled() && element) {
+    // Only want to do the different layout if placeElement has been called.
+    if (const auto* canvas =
+            DynamicTo<HTMLCanvasElement>(element->parentElement())) {
+      return canvas->HasPlacedElements();
+    }
+  }
+
+  return false;
+}
+
 static void AdjustStyleForDisplay(ComputedStyleBuilder& builder,
                                   const ComputedStyle& layout_parent_style,
                                   const Element* element,
                                   Document* document) {
-  // Blockify the children of flex, grid, math or LayoutCustom containers.
-  if (layout_parent_style.BlockifiesChildren() && !HostIsInputFile(element)) {
+  bool is_canvas_placed_element = IsCanvasPlacedElement(element);
+
+  if ((layout_parent_style.BlockifiesChildren() && !HostIsInputFile(element)) ||
+      is_canvas_placed_element) {
     builder.SetIsInBlockifyingDisplay();
     if (builder.Display() != EDisplay::kContents) {
       builder.SetDisplay(EquivalentBlockDisplay(builder.Display()));
@@ -640,9 +658,13 @@
       }
     }
     if (layout_parent_style.IsDisplayFlexibleOrGridBox() ||
-        layout_parent_style.IsDisplayMathType()) {
+        layout_parent_style.IsDisplayMathType() || is_canvas_placed_element) {
       builder.SetIsInsideDisplayIgnoringFloatingChildren();
     }
+
+    if (is_canvas_placed_element) {
+      builder.SetPosition(EPosition::kStatic);
+    }
   }
 
   // We need to avoid to inlinify children of a <fieldset>, which creates a
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 3f31866b..ac94cb2 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
@@ -1668,6 +1668,18 @@
          computed_grid_track_list.IsSubgriddedAxis());
 }
 
+std::optional<Length> StyleBuilderConverter::ConvertMasonrySlack(
+    const StyleResolverState& state,
+    const CSSValue& value) {
+  auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
+  if (identifier_value &&
+      identifier_value->GetValueID() == CSSValueID::kNormal) {
+    return std::nullopt;
+  }
+
+  return ConvertLength(state, value);
+}
+
 StyleHyphenateLimitChars StyleBuilderConverter::ConvertHyphenateLimitChars(
     StyleResolverState& state,
     const CSSValue& value) {
@@ -2468,10 +2480,16 @@
         ResolveColorValue(length_resolver, relative_color_value->OriginColor(),
                           text_link_colors, used_color_scheme, color_provider,
                           is_in_web_app_scope, for_visited_link);
-    return StyleColor(MakeGarbageCollected<StyleColor::UnresolvedRelativeColor>(
-        origin_color, relative_color_value->ColorInterpolationSpace(),
-        relative_color_value->Channel0(), relative_color_value->Channel1(),
-        relative_color_value->Channel2(), relative_color_value->Alpha()));
+    const StyleColor::UnresolvedRelativeColor* unresolved_relative_color =
+        MakeGarbageCollected<StyleColor::UnresolvedRelativeColor>(
+            origin_color, relative_color_value->ColorInterpolationSpace(),
+            relative_color_value->Channel0(), relative_color_value->Channel1(),
+            relative_color_value->Channel2(), relative_color_value->Alpha());
+    if (origin_color.IsAbsoluteColor()) {
+      return StyleColor(unresolved_relative_color->Resolve(Color()));
+    } else {
+      return StyleColor(unresolved_relative_color);
+    }
   }
 
   auto& light_dark_pair = To<CSSLightDarkValuePair>(value);
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
index 3f9704e..7de8049e 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
@@ -202,6 +202,8 @@
                                             const CSSValue&);
   static NGGridTrackList ConvertGridTrackSizeList(StyleResolverState&,
                                                   const CSSValue&);
+  static std::optional<Length> ConvertMasonrySlack(const StyleResolverState&,
+                                                   const CSSValue&);
   static StyleHyphenateLimitChars ConvertHyphenateLimitChars(
       StyleResolverState&,
       const CSSValue&);
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 96e0431..5e2d9da 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -8719,24 +8719,23 @@
   }
 }
 
-ScriptValue Element::requestPointerLock(ScriptState* script_state,
-                                        const PointerLockOptions* options,
-                                        ExceptionState& exception_state) {
+ScriptPromise<IDLUndefined> Element::requestPointerLock(
+    ScriptState* script_state,
+    const PointerLockOptions* options,
+    ExceptionState& exception_state) {
   if (!GetDocument().GetPage()) {
     return ScriptPromise<IDLUndefined>::RejectWithDOMException(
-               script_state, MakeGarbageCollected<DOMException>(
-                                 DOMExceptionCode::kWrongDocumentError,
-                                 "PointerLock cannot be request when there "
-                                 "is no frame or that frame has no page."))
-        .AsScriptValue();
+        script_state, MakeGarbageCollected<DOMException>(
+                          DOMExceptionCode::kWrongDocumentError,
+                          "PointerLock cannot be requested when there "
+                          "is no frame or that frame has no page."));
   }
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(
       script_state, exception_state.GetContext());
-  auto promise = resolver->Promise();
   GetDocument().GetPage()->GetPointerLockController().RequestPointerLock(
       resolver, this, options);
-  return promise.AsScriptValue();
+  return resolver->Promise();
 }
 
 SpellcheckAttributeState Element::GetSpellcheckAttributeState() const {
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 5552c0c..a9928b7c 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1246,9 +1246,10 @@
   }
   void SetIsInTopLayer(bool);
 
-  ScriptValue requestPointerLock(ScriptState* script_state,
-                                 const PointerLockOptions* options,
-                                 ExceptionState& exception_state);
+  ScriptPromise<IDLUndefined> requestPointerLock(
+      ScriptState* script_state,
+      const PointerLockOptions* options,
+      ExceptionState& exception_state);
 
   bool IsSpellCheckingEnabled() const;
 
diff --git a/third_party/blink/renderer/core/dom/element.idl b/third_party/blink/renderer/core/dom/element.idl
index af6fabeb..5f6faf2 100644
--- a/third_party/blink/renderer/core/dom/element.idl
+++ b/third_party/blink/renderer/core/dom/element.idl
@@ -114,7 +114,7 @@
     // Pointer Lock
     // https://w3c.github.io/pointerlock/#extensions-to-the-element-interface
     // https://github.com/w3c/pointerlock/pull/49
-    [MeasureAs=ElementRequestPointerLock, CallWith=ScriptState, RaisesException] any requestPointerLock(optional PointerLockOptions options = {});
+    [MeasureAs=ElementRequestPointerLock, CallWith=ScriptState, RaisesException] Promise<undefined> requestPointerLock(optional PointerLockOptions options = {});
 
     // CSSOM View Module
     // https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface
diff --git a/third_party/blink/renderer/core/editing/commands/apply_style_command_test.cc b/third_party/blink/renderer/core/editing/commands/apply_style_command_test.cc
index 1077b8d..ecd6dd37 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_style_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_style_command_test.cc
@@ -157,8 +157,10 @@
       ApplyStyleCommand::kForceBlockProperties)
       ->Apply();
 
-  EXPECT_EQ("<div style=\"text-align: center;\">|<br>x</div>",
-            GetSelectionTextFromBody());
+  EXPECT_EQ(
+      "<div style=\"text-align: center;\">|x</div><div "
+      "contenteditable=\"false\"></div>",
+      GetSelectionTextFromBody());
 }
 
 // This is a regression test for https://crbug.com/1199902
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
index 1aad69f..42fa63a4a 100644
--- a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
@@ -1241,12 +1241,30 @@
 
   Position downstream_end =
       MostForwardCaretPosition(selection_to_delete_.End());
+  const Node* downstream_container_node = downstream_end.ComputeContainerNode();
+  const Element* downstream_container_root_element =
+      RootEditableElement(*downstream_container_node);
   bool root_will_stay_open_without_placeholder =
-      downstream_end.ComputeContainerNode() ==
-          RootEditableElement(*downstream_end.ComputeContainerNode()) ||
-      (downstream_end.ComputeContainerNode()->IsTextNode() &&
-       downstream_end.ComputeContainerNode()->parentNode() ==
-           RootEditableElement(*downstream_end.ComputeContainerNode()));
+      downstream_container_node == downstream_container_root_element;
+
+  // Check to determine if the root will stay open without a placeholder.
+  // This is done by checking if the downstream end is within a root editable
+  // element that has an inline layout object, or if the downstream end's
+  // container node is within a shadow host that is a text control.
+  if (RuntimeEnabledFeatures::
+          RootElementWithPlaceHolderAfterDeletingSelectionEnabled()) {
+    root_will_stay_open_without_placeholder |=
+        (downstream_container_root_element &&
+         downstream_container_root_element->GetLayoutObject() &&
+         downstream_container_root_element->GetLayoutObject()->IsInline()) ||
+        (downstream_container_node->OwnerShadowHost() &&
+         downstream_container_node->OwnerShadowHost()->IsTextControl());
+  } else {
+    root_will_stay_open_without_placeholder |=
+        (downstream_container_node->IsTextNode() &&
+         downstream_container_node->parentNode() ==
+             downstream_container_root_element);
+  }
   VisiblePosition visible_start = CreateVisiblePosition(
       selection_to_delete_.Start(),
       selection_to_delete_.IsRange() ? TextAffinity::kDownstream : affinity);
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc
index 1343a2ac..c4264b07 100644
--- a/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command_test.cc
@@ -140,12 +140,13 @@
                              .SetSanitizeMarkup(true)
                              .Build());
   // Should not crash.
-  EXPECT_TRUE(command.Apply());
+  // Editing state is aborted after the body stops being editable.
+  EXPECT_FALSE(command.Apply());
 
   // The command removes the <style>, so the <body> stops being editable,
   // and then "x" is not removed.
   EXPECT_FALSE(IsEditable(*GetDocument().body()));
-  EXPECT_EQ("|x", GetSelectionTextFromBody());
+  EXPECT_EQ("^x|", GetSelectionTextFromBody());
 }
 
 // This is a regression test for https://crbug.com/1307391
diff --git a/third_party/blink/renderer/core/editing/commands/typing_command_test.cc b/third_party/blink/renderer/core/editing/commands/typing_command_test.cc
index a4e447f..38788ca 100644
--- a/third_party/blink/renderer/core/editing/commands/typing_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/typing_command_test.cc
@@ -98,7 +98,7 @@
   TypingCommand::InsertText(
       GetDocument(), " ", 0,
       TypingCommand::TextCompositionType::kTextCompositionUpdate, true);
-  EXPECT_EQ("<div contenteditable>^<h1></h1>|</div>",
+  EXPECT_EQ("<div contenteditable><h1>\xC2\xA0|</h1></div>",
             GetSelectionTextFromBody());
 }
 
diff --git a/third_party/blink/renderer/core/events/message_event.cc b/third_party/blink/renderer/core/events/message_event.cc
index cde061c..9d18ccfe 100644
--- a/third_party/blink/renderer/core/events/message_event.cc
+++ b/third_party/blink/renderer/core/events/message_event.cc
@@ -134,7 +134,8 @@
       ports_(ports),
       user_activation_(user_activation) {
   DCHECK(IsValidSource(source_.Get()));
-  serialized_data_memory_accounter_.Register(SizeOfExternalMemoryInBytes());
+  serialized_data_memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                             SizeOfExternalMemoryInBytes());
 }
 
 MessageEvent::MessageEvent(
@@ -156,7 +157,8 @@
       user_activation_(user_activation),
       delegated_capability_(delegated_capability) {
   DCHECK(IsValidSource(source_.Get()));
-  serialized_data_memory_accounter_.Register(SizeOfExternalMemoryInBytes());
+  serialized_data_memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                             SizeOfExternalMemoryInBytes());
 }
 
 MessageEvent::MessageEvent(const String& origin, EventTarget* source)
@@ -172,7 +174,8 @@
       data_type_(kDataTypeString),
       data_as_string_(data),
       origin_(origin) {
-  serialized_data_memory_accounter_.Register(SizeOfExternalMemoryInBytes());
+  serialized_data_memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                             SizeOfExternalMemoryInBytes());
 }
 
 MessageEvent::MessageEvent(Blob* data, const String& origin)
@@ -180,7 +183,8 @@
       data_type_(kDataTypeBlob),
       data_as_blob_(data),
       origin_(origin) {
-  serialized_data_memory_accounter_.Register(SizeOfExternalMemoryInBytes());
+  serialized_data_memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                             SizeOfExternalMemoryInBytes());
 }
 
 MessageEvent::MessageEvent(DOMArrayBuffer* data, const String& origin)
@@ -188,7 +192,12 @@
       data_type_(kDataTypeArrayBuffer),
       data_as_array_buffer_(data),
       origin_(origin) {
-  serialized_data_memory_accounter_.Register(SizeOfExternalMemoryInBytes());
+  serialized_data_memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                             SizeOfExternalMemoryInBytes());
+}
+
+MessageEvent::~MessageEvent() {
+  serialized_data_memory_accounter_.Clear(v8::Isolate::GetCurrent());
 }
 
 MessageEvent* MessageEvent::Create(const AtomicString& type,
@@ -257,7 +266,8 @@
   is_ports_dirty_ = true;
   user_activation_ = user_activation;
   delegated_capability_ = delegated_capability;
-  serialized_data_memory_accounter_.Register(SizeOfExternalMemoryInBytes());
+  serialized_data_memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                             SizeOfExternalMemoryInBytes());
 }
 
 void MessageEvent::initMessageEvent(const AtomicString& type,
@@ -281,7 +291,8 @@
   source_ = source;
   ports_ = ports;
   is_ports_dirty_ = true;
-  serialized_data_memory_accounter_.Register(SizeOfExternalMemoryInBytes());
+  serialized_data_memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                             SizeOfExternalMemoryInBytes());
 }
 
 ScriptValue MessageEvent::data(ScriptState* script_state) {
@@ -306,7 +317,7 @@
         // The data is put on the V8 GC heap here, and therefore the V8 GC does
         // the accounting from here on. We unregister the registered memory to
         // avoid double accounting.
-        serialized_data_memory_accounter_.Unregister();
+        serialized_data_memory_accounter_.Clear(isolate);
         MessagePortArray message_ports = ports();
         SerializedScriptValue::DeserializeOptions options;
         options.message_ports = &message_ports;
diff --git a/third_party/blink/renderer/core/events/message_event.h b/third_party/blink/renderer/core/events/message_event.h
index 10384b7..969f9cc 100644
--- a/third_party/blink/renderer/core/events/message_event.h
+++ b/third_party/blink/renderer/core/events/message_event.h
@@ -35,7 +35,6 @@
 #include "third_party/blink/public/mojom/messaging/delegated_capability.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
-#include "third_party/blink/renderer/bindings/core/v8/serialization/v8_external_memory_accounter.h"
 #include "third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
@@ -43,6 +42,7 @@
 #include "third_party/blink/renderer/core/fileapi/blob.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
+#include "third_party/blink/renderer/platform/bindings/v8_external_memory_accounter.h"
 #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
@@ -133,7 +133,7 @@
   MessageEvent(const String& data, const String& origin);
   MessageEvent(Blob* data, const String& origin);
   MessageEvent(DOMArrayBuffer* data, const String& origin);
-  ~MessageEvent() override = default;
+  ~MessageEvent() override;
 
   void initMessageEvent(const AtomicString& type,
                         bool bubbles,
diff --git a/third_party/blink/renderer/core/frame/deprecation/deprecation.json5 b/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
index f480ab9..ce3e95fe 100644
--- a/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
+++ b/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
@@ -613,6 +613,15 @@
       chrome_status_feature: 5579556305502208,
     },
     {
+      name: "V8GPUAdapter_RequestAdapterInfo_Method",
+      message: "The GPUAdapter `requestAdapterInfo()` method is deprecated, instead use the GPUAdapter `info` attribute.",
+      translation_note: "This warning occurs when the website attempts to invoke the deprecated GPUAdapter `requestAdapterInfo()` method.",
+      web_features: [
+        "kV8GPUAdapter_RequestAdapterInfo_Method",
+      ],
+      chrome_status_feature: 5140787340509184,
+    },
+    {
       name: "V8SharedArrayBufferConstructedInExtensionWithoutIsolation",
       message: "Extensions should opt into cross-origin isolation to continue using `SharedArrayBuffer`. See https://developer.chrome.com/docs/extensions/mv3/cross-origin-isolation/.",
       translation_note: "A deprecation warning shown in the DevTools Issues tab. The placeholder is always the noun 'SharedArrayBuffer' which refers to a JavaScript construct. 'Extensions' refers to Chrome extensions. The warning is shown when Chrome Extensions attempt to use 'SharedArrayBuffer's under insecure circumstances.",
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index 62b1e87..a804a4bf 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -1026,10 +1026,11 @@
   }
 }
 
-bool DOMWindow::IsAccessBlockedByCoopRestrictProperties(
-    v8::Isolate* isolate) const {
+std::optional<DOMWindow::ProxyAccessBlockedReason>
+DOMWindow::GetProxyAccessBlockedReason(v8::Isolate* isolate) const {
   if (!GetFrame()) {
-    return false;
+    // Proxy is disconnected so we cannot take any action anyway.
+    return std::nullopt;
   }
 
   LocalDOMWindow* accessing_window = CurrentDOMWindow(isolate);
@@ -1037,30 +1038,47 @@
 
   LocalFrame* accessing_frame = accessing_window->GetFrame();
   if (!accessing_frame) {
-    return false;
+    // Context is disconnected so we cannot take any action anyway.
+    return std::nullopt;
   }
 
-  // If the two windows are not in the same CoopRelatedGroup, we should not
-  // restrict window proxy access here. This prevents restricting things that
-  // were not meant to. These are the cross browsing context group  accesses
-  // that already existed before COOP: restrict-properties.
+  // Returns an exception message if this window proxy or the window accessing
+  // are not in the same page and one is in a partitioned popin. We check this
+  // case first as it overlaps with the COOP:RP case below.
+  // See https://explainers-by-googlers.github.io/partitioned-popins/
+  if (GetFrame()->GetPage() != accessing_frame->GetPage() &&
+      (accessing_frame->GetPage()->IsPartitionedPopin() ||
+       GetFrame()->GetPage()->IsPartitionedPopin())) {
+    return DOMWindow::ProxyAccessBlockedReason::kPartitionedPopins;
+  }
+
+  // Returns an exception message if the two windows are in the same
+  // CoopRelatedGroup but not in the same BrowsingInstance as this means COOP:
+  // restrict-properties is blocking access between the contexts.
   // TODO(https://crbug.com/1464618): Is there actually any scenario where
   // cross browsing context group was allowed before COOP: restrict-properties?
   // Verify that we need to have this check.
-  if (accessing_frame->GetPage()->CoopRelatedGroupToken() !=
-      GetFrame()->GetPage()->CoopRelatedGroupToken()) {
-    return false;
+  if (accessing_frame->GetPage()->CoopRelatedGroupToken() ==
+          GetFrame()->GetPage()->CoopRelatedGroupToken() &&
+      accessing_frame->GetPage()->BrowsingContextGroupToken() !=
+          GetFrame()->GetPage()->BrowsingContextGroupToken()) {
+    return DOMWindow::ProxyAccessBlockedReason::kCoopRp;
   }
 
-  // If we're dealing with an actual COOP: restrict-properties case, then
-  // compare the BrowsingInstance tokens. If they are different, the access
-  // should be restricted.
-  if (accessing_frame->GetPage()->BrowsingContextGroupToken() !=
-      GetFrame()->GetPage()->BrowsingContextGroupToken()) {
-    return true;
-  }
+  // Our fallback allows access.
+  return std::nullopt;
+}
 
-  return false;
+// static
+String DOMWindow::GetProxyAccessBlockedExceptionMessage(
+    DOMWindow::ProxyAccessBlockedReason reason) {
+  switch (reason) {
+    case ProxyAccessBlockedReason::kCoopRp:
+      return "Cross-Origin-Opener-Policy: 'restrict-properties' blocked the "
+             "access.";
+    case ProxyAccessBlockedReason::kPartitionedPopins:
+      return "Partitioned Popin blocked the access.";
+  }
 }
 
 void DOMWindow::PostedMessage::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/frame/dom_window.h b/third_party/blink/renderer/core/frame/dom_window.h
index 3dc3683..0225048d 100644
--- a/third_party/blink/renderer/core/frame/dom_window.h
+++ b/third_party/blink/renderer/core/frame/dom_window.h
@@ -172,12 +172,13 @@
       mojom::blink::WebFeature property_access_from_other_page,
       mojom::blink::WindowProxyAccessType access_type) const;
 
-  // Returns whether access should be limited by Cross-Origin-Opener-Policy:
-  // restrict-properties. This is the case for pages in the same
-  // CoopRelatedGroup that can reach each other WindowProxies but do not belong
-  // to the same browsing context group. `isolate` represents the isolate in
-  // which the Window access is taking place.
-  bool IsAccessBlockedByCoopRestrictProperties(v8::Isolate* isolate) const;
+  // We need to check proxy access to see if it's blocked, and if so whether
+  // it's for COOP-RP issues or Partitioned Popin issues.
+  enum class ProxyAccessBlockedReason { kCoopRp, kPartitionedPopins };
+  std::optional<ProxyAccessBlockedReason> GetProxyAccessBlockedReason(
+      v8::Isolate* isolate) const;
+  static String GetProxyAccessBlockedExceptionMessage(
+      ProxyAccessBlockedReason reason);
 
  protected:
   explicit DOMWindow(Frame&);
diff --git a/third_party/blink/renderer/core/frame/performance_monitor.cc b/third_party/blink/renderer/core/frame/performance_monitor.cc
index f561ae6..85ca5d9 100644
--- a/third_party/blink/renderer/core/frame/performance_monitor.cc
+++ b/third_party/blink/renderer/core/frame/performance_monitor.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "third_party/blink/renderer/core/frame/performance_monitor.h"
 
 #include "base/format_macros.h"
diff --git a/third_party/blink/renderer/core/frame/performance_monitor.h b/third_party/blink/renderer/core/frame/performance_monitor.h
index 9e666e41..c88f658a 100644
--- a/third_party/blink/renderer/core/frame/performance_monitor.h
+++ b/third_party/blink/renderer/core/frame/performance_monitor.h
@@ -158,7 +158,7 @@
   unsigned user_callback_depth_ = 0;
   const void* user_callback_;
 
-  base::TimeDelta thresholds_[kAfterLast];
+  std::array<base::TimeDelta, kAfterLast> thresholds_;
 
   Member<LocalFrame> local_root_;
   Member<ExecutionContext> task_execution_context_;
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5
index cc212e07..c6f72eb 100644
--- a/third_party/blink/renderer/core/frame/settings.json5
+++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -543,7 +543,7 @@
     // Only used by web tests.
     {
       name: "windowShowState",
-      initial: "ui::WindowShowState::SHOW_STATE_DEFAULT",
+      initial: "ui::SHOW_STATE_DEFAULT",
       invalidate: ["MediaQuery"],
       type: "ui::WindowShowState",
       include_paths: ["ui/base/ui_base_types.h"],
diff --git a/third_party/blink/renderer/core/frame/window_properties.cc b/third_party/blink/renderer/core/frame/window_properties.cc
index 7853d5f5..fe185c6 100644
--- a/third_party/blink/renderer/core/frame/window_properties.cc
+++ b/third_party/blink/renderer/core/frame/window_properties.cc
@@ -26,14 +26,9 @@
     return v8::Local<v8::Value>();
   }
 
-  // Verify that COOP: restrict-properties does not prevent this access.
-  // TODO(https://crbug.com/1467216): This will block all same-origin only
-  // properties accesses with a "Named property" access failure, because the
-  // properties will be tried here as part of the algorithm. See if we need to
-  // have a custom message in that case, possibly by actually printing the
-  // passed name.
   v8::Isolate* isolate = frame->GetWindowProxyManager()->GetIsolate();
-  if (window->IsAccessBlockedByCoopRestrictProperties(isolate)) [[unlikely]] {
+
+  if (auto reason = window->GetProxyAccessBlockedReason(isolate)) [[unlikely]] {
     // We need to not throw an exception if we're dealing with the special
     // "then" property but return undefined instead. See
     // https://html.spec.whatwg.org/#crossoriginpropertyfallback-(-p-). This
@@ -46,9 +41,7 @@
                                    "Window", name,
                                    ExceptionState::kForInterceptor);
     exception_state.ThrowSecurityError(
-        "Cross-Origin-Opener-Policy: 'restrict-properties' blocked the access.",
-        "Cross-Origin-Opener-Policy: 'restrict-properties' blocked the "
-        "access.");
+        DOMWindow::GetProxyAccessBlockedExceptionMessage(*reason));
     return v8::Null(isolate);
   }
 
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 87a5608..ae18272 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -1930,4 +1930,14 @@
   return GetRasterMode() == RasterMode::kGPU;
 }
 
+void HTMLCanvasElement::SetHasPlacedElements() {
+  // If this is the first time placeElement() is called, its possible that the
+  // canvas contains fallback content that has been ignored and needs to be
+  // laid out.
+  if (!has_placed_elements_) {
+    has_placed_elements_ = true;
+    SetForceReattachLayoutTree();
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
index 8de96d2..579277c 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -282,6 +282,10 @@
 
   void UpdateSuspendOffscreenCanvasAnimation();
 
+  void SetHasPlacedElements();
+
+  bool HasPlacedElements() const { return has_placed_elements_; }
+
   // Gets the settings of this Html Canvas Element. If there is a frame, it will
   // return the settings from the frame. If it is a frameless element it will
   // try to fetch the global dom window and get the settings from there.
@@ -421,6 +425,10 @@
   mutable intptr_t externally_allocated_memory_;
 
   scoped_refptr<StaticBitmapImage> transparent_image_;
+
+  // When the underlying context uses placeElement() layout needs to be run on
+  // the fallback content.
+  bool has_placed_elements_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_meta_element.cc b/third_party/blink/renderer/core/html/html_meta_element.cc
index 01289f2c..bf98185 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element.cc
@@ -539,7 +539,9 @@
       TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseMetaViewport",
       "data", [&](perfetto::TracedValue context) {
         auto dict = std::move(context).WriteDictionary();
-        dict.Add("frame", GetDocument().GetFrame()->GetFrameIdForTracing());
+        if (GetDocument().GetFrame()) {
+          dict.Add("frame", GetDocument().GetFrame()->GetFrameIdForTracing());
+        }
         dict.Add("node_id", GetDomNodeId());
         dict.Add("content", content);
       });
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index af2f4f9..8bbaf85 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -464,6 +464,8 @@
 
   if (prefetch_policy == kAllowPrefetching)
     preloader_ = MakeGarbageCollected<HTMLResourcePreloader>(document);
+
+  should_skip_preload_scan_ = ShouldSkipPreloadScan();
 }
 
 HTMLDocumentParser::~HTMLDocumentParser() {
@@ -847,7 +849,8 @@
   if (is_stopped_or_parsing_fragment)
     return false;
 
-  if (IsPaused() && preloader_ && !background_scanner_) {
+  if (IsPaused() && preloader_ && !background_scanner_ &&
+      !should_skip_preload_scan_) {
     if (!preload_scanner_) {
       preload_scanner_ =
           CreatePreloadScanner(TokenPreloadScanner::ScannerType::kMainDocument);
@@ -958,7 +961,7 @@
   // Call EndIfDelayed manually at the end to maintain preload behaviour.
   PumpTokenizerIfPossible();
 
-  if (IsPaused()) {
+  if (IsPaused() && !should_skip_preload_scan_) {
     // Check the document.write() output with a separate preload scanner as
     // the main scanner can't deal with insertions.
     if (!insertion_preload_scanner_) {
@@ -987,7 +990,7 @@
   ScanInBackground(input_source);
 
   if (!background_scanner_ && !preload_scanner_ && preloader_ &&
-      GetDocument()->Url().IsValid() &&
+      GetDocument()->Url().IsValid() && !should_skip_preload_scan_ &&
       (!task_runner_state_->IsSynchronous() ||
        GetDocument()->IsPrefetchOnly() || IsPaused())) {
     // If we're operating with a budget, we need to create a preload scanner to
@@ -1603,7 +1606,7 @@
       // TODO(crbug.com/1329535): Support scanning prefetch documents in the
       // background.
       !GetDocument()->IsPrefetchOnly() &&
-      IsPreloadScanningEnabled(GetDocument())) {
+      IsPreloadScanningEnabled(GetDocument()) && !should_skip_preload_scan_) {
     // The background scanner should never be created if a main thread scanner
     // is already available.
     DCHECK(!preload_scanner_);
@@ -1749,4 +1752,19 @@
   return tokens_parsed % 10 == 0;
 }
 
+bool HTMLDocumentParser::ShouldSkipPreloadScan() {
+  // Check if Document-Policy has Expect-No-Embedded-Resources hint.
+  auto* document = GetDocument();
+  if (const auto* context = document->GetExecutionContext()) {
+    if (context->IsFeatureEnabled(
+            mojom::blink::DocumentPolicyFeature::kExpectNoEmbeddedResources)) {
+      UseCounter::Count(document,
+                        WebFeature::kDocumentPolicyExpectNoEmbeddedResources);
+      return true;
+    }
+  }
+
+  return false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index e47446b..7268f28 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -254,6 +254,8 @@
                              int newly_consumed_characters,
                              int tokens_parsed) const;
 
+  bool ShouldSkipPreloadScan();
+
   HTMLInputStream input_;
   const HTMLParserOptions options_;
   Member<HTMLParserReentryPermit> reentry_permit_ =
@@ -292,6 +294,9 @@
 
   // Set to true if PumpTokenizer() was called at least once.
   bool did_pump_tokenizer_ = false;
+
+  // Cached result of ShouldSkipPreloadScan()
+  bool should_skip_preload_scan_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index aa86a25..0b1e02a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -239,6 +239,8 @@
       return protocol::DOM::PseudoTypeEnum::ScrollNextButton;
     case kPseudoIdScrollPrevButton:
       return protocol::DOM::PseudoTypeEnum::ScrollPrevButton;
+    case kPseudoIdColumn:
+      return protocol::DOM::PseudoTypeEnum::Column;
     case kPseudoIdResizer:
       return protocol::DOM::PseudoTypeEnum::Resizer;
     case kPseudoIdInputListButton:
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 562ff0d..88efd3b 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -367,6 +367,7 @@
     DEFINE_STRING_MAPPING(PseudoScrollMarkerGroup)
     DEFINE_STRING_MAPPING(PseudoScrollNextButton)
     DEFINE_STRING_MAPPING(PseudoScrollPrevButton)
+    DEFINE_STRING_MAPPING(PseudoColumn)
     DEFINE_STRING_MAPPING(PseudoWindowInactive)
     DEFINE_STRING_MAPPING(PseudoCornerPresent)
     DEFINE_STRING_MAPPING(PseudoDecrement)
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow_hot.cc b/third_party/blink/renderer/core/layout/layout_block_flow_hot.cc
index bef389d..b3fc758b 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow_hot.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow_hot.cc
@@ -30,6 +30,11 @@
     return true;
   }
 
+  if (RuntimeEnabledFeatures::CanvasPlaceElementEnabled() &&
+      Parent()->IsCanvas()) {
+    return true;
+  }
+
   if (RuntimeEnabledFeatures::ContainerTypeNoLayoutContainmentEnabled()) {
     if (StyleRef().IsContainerForSizeContainerQueries()) {
       return true;
diff --git a/third_party/blink/renderer/core/layout/layout_html_canvas.cc b/third_party/blink/renderer/core/layout/layout_html_canvas.cc
index 4c27ae7..0ed7c66 100644
--- a/third_party/blink/renderer/core/layout/layout_html_canvas.cc
+++ b/third_party/blink/renderer/core/layout/layout_html_canvas.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
+#include "third_party/blink/renderer/core/layout/layout_replaced.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/paint/html_canvas_painter.h"
@@ -107,4 +108,17 @@
   To<HTMLCanvasElement>(GetNode())->LayoutObjectDestroyed();
 }
 
+void LayoutHTMLCanvas::Trace(Visitor* visitor) const {
+  visitor->Trace(children_);
+  LayoutReplaced::Trace(visitor);
+}
+
+bool LayoutHTMLCanvas::IsChildAllowed(LayoutObject* child,
+                                      const ComputedStyle& style) const {
+  NOT_DESTROYED();
+  return IsA<Element>(GetNode()) && !child->IsText() &&
+         To<HTMLCanvasElement>(GetNode())->HasPlacedElements() &&
+         RuntimeEnabledFeatures::CanvasPlaceElementEnabled();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_html_canvas.h b/third_party/blink/renderer/core/layout/layout_html_canvas.h
index 7b04b6f3..67aa5a67 100644
--- a/third_party/blink/renderer/core/layout/layout_html_canvas.h
+++ b/third_party/blink/renderer/core/layout/layout_html_canvas.h
@@ -57,13 +57,55 @@
 
   void WillBeDestroyed() override;
 
+  void Trace(Visitor*) const override;
+
+  LayoutObject* FirstChild() const {
+    NOT_DESTROYED();
+    DCHECK_EQ(Children(), VirtualChildren());
+    return Children()->FirstChild();
+  }
+  LayoutObject* LastChild() const {
+    NOT_DESTROYED();
+    DCHECK_EQ(Children(), VirtualChildren());
+    return Children()->LastChild();
+  }
+
+  // As with LayoutMedia, use firstChild or lastChild instead.
+  void SlowFirstChild() const = delete;
+  void SlowLastChild() const = delete;
+
+  const LayoutObjectChildList* Children() const {
+    NOT_DESTROYED();
+    return &children_;
+  }
+  LayoutObjectChildList* Children() {
+    NOT_DESTROYED();
+    return &children_;
+  }
+
  private:
+  LayoutObjectChildList* VirtualChildren() final {
+    NOT_DESTROYED();
+    return Children();
+  }
+  const LayoutObjectChildList* VirtualChildren() const final {
+    NOT_DESTROYED();
+    return Children();
+  }
+  bool CanHaveChildren() const final {
+    NOT_DESTROYED();
+    return RuntimeEnabledFeatures::CanvasPlaceElementEnabled();
+  }
+  bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const final;
+
   void PaintReplaced(const PaintInfo&,
                      const PhysicalOffset& paint_offset) const override;
   void IntrinsicSizeChanged() override {
     NOT_DESTROYED();
     CanvasSizeChanged();
   }
+
+  LayoutObjectChildList children_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/layout/layout_input_node.h b/third_party/blink/renderer/core/layout/layout_input_node.h
index e46e873..8bc9910 100644
--- a/third_party/blink/renderer/core/layout/layout_input_node.h
+++ b/third_party/blink/renderer/core/layout/layout_input_node.h
@@ -107,6 +107,7 @@
   bool IsFieldsetContainer() const { return IsBlock() && box_->IsFieldset(); }
   bool IsInitialLetterBox() const { return box_->IsInitialLetterBox(); }
   bool IsMedia() const { return box_->IsMedia(); }
+  bool IsCanvas() const { return box_->IsCanvas(); }
   bool IsRubyColumn() const { return IsBlock() && box_->IsRubyColumn(); }
   bool IsRubyText() const { return box_->IsRubyText(); }
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 5c3e9359..565eba8b 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -3524,7 +3524,7 @@
   if (!skip_info.AncestorSkipped())
     container->MapAncestorToLocal(ancestor, transform_state, mode);
 
-  PhysicalOffset container_offset = OffsetFromContainer(container);
+  PhysicalOffset container_offset = OffsetFromContainer(container, mode);
   bool use_transforms = !(mode & kIgnoreTransforms);
 
   // Just because container and this have preserve-3d doesn't mean all
diff --git a/third_party/blink/renderer/core/layout/layout_object_test.cc b/third_party/blink/renderer/core/layout/layout_object_test.cc
index 72f0259f..6f6b002 100644
--- a/third_party/blink/renderer/core/layout/layout_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_test.cc
@@ -1801,4 +1801,40 @@
                            ->ContainingScrollContainer());
 }
 
+TEST_F(LayoutObjectTest, ScrollOffsetMapping) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="scroller" style="overflow:scroll; width:300px; height:300px;">
+      <div id="inner" style="width:1000px; height:1000px; margin:50px;"></div>
+    </div>
+    <div style="width:200vw; height:200vh;"></div>
+  )HTML");
+
+  Element* scroller = GetElementById("scroller");
+  ASSERT_TRUE(scroller);
+  scroller->scrollTo(100, 200);
+  GetDocument().View()->LayoutViewport()->SetScrollOffset(
+      ScrollOffset(10, 20), mojom::blink::ScrollType::kProgrammatic);
+  UpdateAllLifecyclePhasesForTest();
+  LayoutObject* inner = GetLayoutObjectByElementId("inner");
+  ASSERT_TRUE(inner);
+
+  // Test with scroll offsets included:
+  gfx::PointF offset;
+  offset = inner->LocalToAncestorPoint(offset, /*ancestor=*/nullptr);
+  EXPECT_EQ(offset, gfx::PointF(-52, -162));
+  // And back again:
+  offset = inner->AncestorToLocalPoint(/*ancestor=*/nullptr, offset);
+  EXPECT_EQ(offset, gfx::PointF());
+
+  // Test with scroll offsets excluded:
+  offset = gfx::PointF();
+  offset = inner->LocalToAncestorPoint(offset, /*ancestor=*/nullptr,
+                                       kIgnoreScrollOffset);
+  EXPECT_EQ(offset, gfx::PointF(58, 58));
+  // And back again:
+  offset = inner->AncestorToLocalPoint(/*ancestor=*/nullptr, offset,
+                                       kIgnoreScrollOffset);
+  EXPECT_EQ(offset, gfx::PointF());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_theme.cc b/third_party/blink/renderer/core/layout/layout_theme.cc
index 35e08dcb..2105d4e2 100644
--- a/third_party/blink/renderer/core/layout/layout_theme.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme.cc
@@ -419,6 +419,10 @@
     case kProgressBarPart:
       return builder.HasAuthorBackground() || builder.HasAuthorBorder();
 
+    case kMeterPart:
+      return RuntimeEnabledFeatures::MeterDevolveAppearanceEnabled() &&
+             (builder.HasAuthorBackground() || builder.HasAuthorBorder());
+
     case kMenulistPart:
     case kSearchFieldPart:
     case kTextAreaPart:
diff --git a/third_party/blink/renderer/core/layout/replaced_layout_algorithm.cc b/third_party/blink/renderer/core/layout/replaced_layout_algorithm.cc
index b9ed35b8..0b7ce5d9 100644
--- a/third_party/blink/renderer/core/layout/replaced_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/replaced_layout_algorithm.cc
@@ -4,10 +4,13 @@
 
 #include "third_party/blink/renderer/core/layout/replaced_layout_algorithm.h"
 
+#include "third_party/blink/renderer/core/layout/constraint_space.h"
+#include "third_party/blink/renderer/core/layout/constraint_space_builder.h"
 #include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/layout/layout_replaced.h"
 #include "third_party/blink/renderer/core/layout/layout_video.h"
+#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 
 namespace blink {
 
@@ -33,6 +36,11 @@
     LayoutMediaChildren();
   }
 
+  if (Node().IsCanvas() &&
+      RuntimeEnabledFeatures::CanvasPlaceElementEnabled()) {
+    LayoutCanvasChildren();
+  }
+
   return container_builder_.ToBoxFragment();
 }
 
@@ -42,6 +50,31 @@
   return MinMaxSizesResult();
 }
 
+// This is necessary for CanvasRenderingContext2D.placeElement().
+void ReplacedLayoutAlgorithm::LayoutCanvasChildren() {
+  for (LayoutInputNode child = Node().FirstChild(); child;
+       child = child.NextSibling()) {
+    DCHECK(!child.IsFloating());
+    DCHECK(!child.IsOutOfFlowPositioned());
+
+    ConstraintSpaceBuilder space_builder(GetConstraintSpace().GetWritingMode(),
+                                         child.Style().GetWritingDirection(),
+                                         /* is_new_fc= */ true);
+
+    space_builder.SetAvailableSize(ChildAvailableSize());
+    space_builder.SetPercentageResolutionSize(ChildAvailableSize());
+    space_builder.SetIsPaintedAtomically(true);
+
+    const LayoutResult* result =
+        To<BlockNode>(child).Layout(space_builder.ToConstraintSpace());
+    // Since this only works with placeElement(), we ignore relative placement
+    // and put the element at (0,0) because it will be placed explicitly by
+    // the user.
+    container_builder_.AddResult(*result,
+                                 LogicalOffset(LayoutUnit(), LayoutUnit()));
+  }
+}
+
 void ReplacedLayoutAlgorithm::LayoutMediaChildren() {
   WritingModeConverter converter(GetConstraintSpace().GetWritingDirection(),
                                  container_builder_.Size());
diff --git a/third_party/blink/renderer/core/layout/replaced_layout_algorithm.h b/third_party/blink/renderer/core/layout/replaced_layout_algorithm.h
index e53ed9ae..6c4bba81 100644
--- a/third_party/blink/renderer/core/layout/replaced_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/replaced_layout_algorithm.h
@@ -24,6 +24,7 @@
 
  private:
   void LayoutMediaChildren();
+  void LayoutCanvasChildren();
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index 1cfdf4a..6c64af1 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -501,6 +501,11 @@
   // We should never be in a state where one of these was set and not the other.
   DCHECK(!partitioned_popin_opener_top_frame_origin_ ==
          !partitioned_popin_opener_site_for_cookies_);
+
+  // The feature must be enabled if a popin top-frame origin was set.
+  DCHECK(RuntimeEnabledFeatures::PartitionedPopinsEnabled() ||
+         !partitioned_popin_opener_top_frame_origin_);
+
   return partitioned_popin_opener_top_frame_origin_.get();
 }
 
@@ -509,6 +514,11 @@
   // We should never be in a state where one of these was set and not the other.
   DCHECK(!partitioned_popin_opener_top_frame_origin_ ==
          !partitioned_popin_opener_site_for_cookies_);
+
+  // The feature must be enabled if a popin site for cookies was set.
+  DCHECK(RuntimeEnabledFeatures::PartitionedPopinsEnabled() ||
+         !partitioned_popin_opener_site_for_cookies_);
+
   return partitioned_popin_opener_site_for_cookies_;
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
index 08a87ab2..d5aaf701 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
 #include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/layout_video.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -31,6 +32,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
 #include "third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "ui/gfx/geometry/point3_f.h"
 
 namespace blink {
@@ -131,6 +133,13 @@
       !paint_layer_.HasSelfPaintingLayerDescendant())
     return kFullyPainted;
 
+  if (auto* node = DynamicTo<Element>(object.GetNode())) {
+    if (node->IsInCanvasSubtree() && !DynamicTo<HTMLCanvasElement>(node)) {
+      // This prevents canvas fallback content from being rendered.
+      return kFullyPainted;
+    }
+  }
+
   std::optional<CheckAncestorPositionVisibilityScope>
       check_position_visibility_scope;
   if (paint_layer_.InvisibleForPositionVisibility() ||
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 27f06bf..9436ada9 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2417,7 +2417,7 @@
           : 0;
 
   PhysicalRect local_expose_rect =
-      GetLayoutBox()->AbsoluteToLocalRect(absolute_rect, flag);
+      GetLayoutBox()->AbsoluteToLocalRect(absolute_rect);
   ScrollOffset target_offset = scroll_into_view_util::GetScrollOffsetToExpose(
       *this, local_expose_rect, scroll_margin, *params->align_x.get(),
       *params->align_y.get());
diff --git a/third_party/blink/renderer/core/paint/replaced_painter.cc b/third_party/blink/renderer/core/paint/replaced_painter.cc
index 3059136..83b266e 100644
--- a/third_party/blink/renderer/core/paint/replaced_painter.cc
+++ b/third_party/blink/renderer/core/paint/replaced_painter.cc
@@ -165,8 +165,9 @@
 
   if (local_paint_info.phase != PaintPhase::kForeground &&
       local_paint_info.phase != PaintPhase::kSelectionDragImage &&
-      !layout_replaced_.CanHaveChildren())
+      (!layout_replaced_.CanHaveChildren() || layout_replaced_.IsCanvas())) {
     return;
+  }
 
   if (local_paint_info.phase == PaintPhase::kSelectionDragImage &&
       !layout_replaced_.IsSelected())
diff --git a/third_party/blink/renderer/core/permissions_policy/document_policy_features.json5 b/third_party/blink/renderer/core/permissions_policy/document_policy_features.json5
index 2122df6..743463f8 100644
--- a/third_party/blink/renderer/core/permissions_policy/document_policy_features.json5
+++ b/third_party/blink/renderer/core/permissions_policy/document_policy_features.json5
@@ -84,6 +84,13 @@
       document_policy_name: "include-js-call-stacks-in-crash-reports",
       value_type: "Bool",
       default_value: "false",
+    },
+    {
+      name: "ExpectNoEmbeddedResources",
+      document_policy_name: "expect-no-embedded-resources",
+      value_type: "Bool",
+      default_value: "false",
+      depends_on: ["DocumentPolicyExpectNoEmbeddedResources"]
     }
   ],
 }
diff --git a/third_party/blink/renderer/core/streams/transferable_streams.cc b/third_party/blink/renderer/core/streams/transferable_streams.cc
index 45d7e33..00dcb2b1 100644
--- a/third_party/blink/renderer/core/streams/transferable_streams.cc
+++ b/third_party/blink/renderer/core/streams/transferable_streams.cc
@@ -788,10 +788,11 @@
         : source_(source), exception_context_(exception_context) {}
 
     ScriptValue Call(ScriptState* script_state, ScriptValue value) override {
-      ExceptionState exception_state(script_state->GetIsolate(),
-                                     exception_context_);
-      return source_->source2_->Pull(script_state, exception_state)
-          .AsScriptValue();
+      v8::Isolate* isolate = script_state->GetIsolate();
+      ExceptionState exception_state(isolate, exception_context_);
+      return ScriptValue(
+          isolate,
+          source_->source2_->Pull(script_state, exception_state).V8Promise());
     }
     void Trace(Visitor* visitor) const override {
       visitor->Trace(source_);
diff --git a/third_party/blink/renderer/core/style/computed_style_constants.h b/third_party/blink/renderer/core/style/computed_style_constants.h
index d3966e85..7a61b39 100644
--- a/third_party/blink/renderer/core/style/computed_style_constants.h
+++ b/third_party/blink/renderer/core/style/computed_style_constants.h
@@ -75,6 +75,7 @@
   kPseudoIdScrollMarkerGroup,
   kPseudoIdScrollNextButton,
   kPseudoIdScrollPrevButton,
+  kPseudoIdColumn,
   kPseudoIdSearchText,
   kPseudoIdTargetText,
   kPseudoIdHighlight,
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index 58a1684..6e758005 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -240,7 +240,7 @@
     {
       name: "PseudoElementStyles",
       field_template: "primitive",
-      field_size: 17,
+      field_size: 18,
       default_value: "kPseudoIdNone",
       type_name: "unsigned",
       reset_on_new_style: true,
diff --git a/third_party/blink/renderer/core/timing/performance_event_timing.cc b/third_party/blink/renderer/core/timing/performance_event_timing.cc
index fd4c6c32..c4d34b7a 100644
--- a/third_party/blink/renderer/core/timing/performance_event_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_event_timing.cc
@@ -138,6 +138,149 @@
   visitor->Trace(target_);
 }
 
+namespace {
+perfetto::protos::pbzero::EventTiming::EventType GetEventType(
+    const AtomicString& name) {
+  using ProtoType = perfetto::protos::pbzero::EventTiming::EventType;
+  if (name == event_type_names::kAuxclick) {
+    return ProtoType::AUX_CLICK_EVENT;
+  }
+  if (name == event_type_names::kClick) {
+    return ProtoType::CLICK_EVENT;
+  }
+  if (name == event_type_names::kContextmenu) {
+    return ProtoType::CONTEXT_MENU_EVENT;
+  }
+  if (name == event_type_names::kDblclick) {
+    return ProtoType::DOUBLE_CLICK_EVENT;
+  }
+  if (name == event_type_names::kMousedown) {
+    return ProtoType::MOUSE_DOWN_EVENT;
+  }
+  if (name == event_type_names::kMouseenter) {
+    return ProtoType::MOUSE_ENTER_EVENT;
+  }
+  if (name == event_type_names::kMouseleave) {
+    return ProtoType::MOUSE_LEAVE_EVENT;
+  }
+  if (name == event_type_names::kMouseout) {
+    return ProtoType::MOUSE_OUT_EVENT;
+  }
+  if (name == event_type_names::kMouseover) {
+    return ProtoType::MOUSE_OVER_EVENT;
+  }
+  if (name == event_type_names::kMouseup) {
+    return ProtoType::MOUSE_UP_EVENT;
+  }
+  if (name == event_type_names::kPointerover) {
+    return ProtoType::POINTER_OVER_EVENT;
+  }
+  if (name == event_type_names::kPointerenter) {
+    return ProtoType::POINTER_ENTER_EVENT;
+  }
+  if (name == event_type_names::kPointerdown) {
+    return ProtoType::POINTER_DOWN_EVENT;
+  }
+  if (name == event_type_names::kPointerup) {
+    return ProtoType::POINTER_UP_EVENT;
+  }
+  if (name == event_type_names::kPointercancel) {
+    return ProtoType::POINTER_CANCEL_EVENT;
+  }
+  if (name == event_type_names::kPointerout) {
+    return ProtoType::POINTER_OUT_EVENT;
+  }
+  if (name == event_type_names::kPointerleave) {
+    return ProtoType::POINTER_LEAVE_EVENT;
+  }
+  if (name == event_type_names::kGotpointercapture) {
+    return ProtoType::GOT_POINTER_CAPTURE_EVENT;
+  }
+  if (name == event_type_names::kLostpointercapture) {
+    return ProtoType::LOST_POINTER_CAPTURE_EVENT;
+  }
+  if (name == event_type_names::kTouchstart) {
+    return ProtoType::TOUCH_START_EVENT;
+  }
+  if (name == event_type_names::kTouchend) {
+    return ProtoType::TOUCH_END_EVENT;
+  }
+  if (name == event_type_names::kTouchcancel) {
+    return ProtoType::TOUCH_CANCEL_EVENT;
+  }
+  if (name == event_type_names::kKeydown) {
+    return ProtoType::KEY_DOWN_EVENT;
+  }
+  if (name == event_type_names::kKeypress) {
+    return ProtoType::KEY_PRESS_EVENT;
+  }
+  if (name == event_type_names::kKeyup) {
+    return ProtoType::KEY_UP_EVENT;
+  }
+  if (name == event_type_names::kBeforeinput) {
+    return ProtoType::BEFORE_INPUT_EVENT;
+  }
+  if (name == event_type_names::kInput) {
+    return ProtoType::INPUT_EVENT;
+  }
+  if (name == event_type_names::kCompositionstart) {
+    return ProtoType::COMPOSITION_START_EVENT;
+  }
+  if (name == event_type_names::kCompositionupdate) {
+    return ProtoType::COMPOSITION_UPDATE_EVENT;
+  }
+  if (name == event_type_names::kCompositionend) {
+    return ProtoType::COMPOSITION_END_EVENT;
+  }
+  if (name == event_type_names::kDragstart) {
+    return ProtoType::DRAG_START_EVENT;
+  }
+  if (name == event_type_names::kDragend) {
+    return ProtoType::DRAG_END_EVENT;
+  }
+  if (name == event_type_names::kDragenter) {
+    return ProtoType::DRAG_ENTER_EVENT;
+  }
+  if (name == event_type_names::kDragleave) {
+    return ProtoType::DRAG_LEAVE_EVENT;
+  }
+  if (name == event_type_names::kDragover) {
+    return ProtoType::DRAG_OVER_EVENT;
+  }
+  if (name == event_type_names::kDrop) {
+    return ProtoType::DROP_EVENT;
+  }
+  return ProtoType::UNDEFINED;
+}
+}  // namespace
+
+void PerformanceEventTiming::SetPerfettoData(
+    Frame* frame,
+    perfetto::protos::pbzero::EventTiming* event_timing,
+    base::TimeTicks time_origin) {
+  event_timing->set_type(GetEventType(name()));
+  event_timing->set_cancelable(cancelable());
+  if (HasKnownInteractionID()) {
+    event_timing->set_interaction_id(interactionId());
+    event_timing->set_interaction_offset(interactionOffset());
+  }
+  event_timing->set_node_id(target_ ? target_->GetDomNodeId()
+                                    : kInvalidDOMNodeId);
+  event_timing->set_frame(GetFrameIdForTracing(frame).Ascii());
+  if (reporting_info_.fallback_time.has_value()) {
+    event_timing->set_fallback_time_us(
+        (reporting_info_.fallback_time.value() - time_origin).InMicroseconds());
+  }
+  if (reporting_info_.key_code.has_value()) {
+    event_timing->set_key_code(reporting_info_.key_code.value());
+  }
+  if (reporting_info_.pointer_id.has_value()) {
+    event_timing->set_pointer_id(reporting_info_.pointer_id.value());
+  }
+}
+
+// TODO(sullivan): Remove this deprecated data when DevTools migrates to the
+// perfetto events.
 std::unique_ptr<TracedValue> PerformanceEventTiming::ToTracedValue(
     Frame* frame) const {
   auto traced_value = std::make_unique<TracedValue>();
diff --git a/third_party/blink/renderer/core/timing/performance_event_timing.h b/third_party/blink/renderer/core/timing/performance_event_timing.h
index 10a9da53..4e7e9587 100644
--- a/third_party/blink/renderer/core/timing/performance_event_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_event_timing.h
@@ -11,6 +11,10 @@
 #include "third_party/blink/renderer/core/timing/performance.h"
 #include "third_party/blink/renderer/core/timing/performance_entry.h"
 
+namespace perfetto::protos::pbzero {
+class EventTiming;
+}  // namespace perfetto::protos::pbzero
+
 namespace blink {
 
 class Frame;
@@ -106,7 +110,12 @@
 
   void Trace(Visitor*) const override;
 
+  // TODO(sullivan): Remove the deprecated TracedValue when DevTools migrates
+  // to the perfetto events.
   std::unique_ptr<TracedValue> ToTracedValue(Frame* frame) const;
+  void SetPerfettoData(Frame* frame,
+                       perfetto::protos::pbzero::EventTiming* traced_value,
+                       base::TimeTicks time_origin);
 
   // Getters and setters of the EventTimingReportingInfo object.
   EventTimingReportingInfo* GetEventTimingReportingInfo() {
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index dfa27e4..4b1cd2a 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -38,6 +38,7 @@
 #include "base/feature_list.h"
 #include "base/trace_event/common/trace_event_common.h"
 #include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_id_helper.h"
 #include "components/viz/common/frame_timing_details.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/network/public/mojom/load_timing_info.mojom-blink.h"
@@ -694,9 +695,58 @@
                  event->GetEventTimingReportingInfo()->presentation_index;
         });
 
-    for (auto it = event_timing_entries_.begin(); it != iter;
-         it = std::next(it)) {
+    auto it = event_timing_entries_.begin();
+    // If the list is empty, early exit.
+    if (it == iter) {
+      continue;
+    }
+
+    bool tracing_enabled;
+    TRACE_EVENT_CATEGORY_GROUP_ENABLED("devtools.timeline", &tracing_enabled);
+    if (tracing_enabled) {
+      TRACE_EVENT_INSTANT(
+          "devtools.timeline", "EventCreation",
+          perfetto::Track::ThreadScoped(this),
+          it->Get()->GetEventTimingReportingInfo()->creation_time,
+          perfetto::Flow::ProcessScoped(presentation_index_to_report));
+      TRACE_EVENT_BEGIN(
+          "devtools.timeline", "EventsInAnimationFrame",
+          perfetto::Track::ThreadScoped(this),
+          it->Get()->GetEventTimingReportingInfo()->processing_start_time,
+          perfetto::Flow::ProcessScoped(presentation_index_to_report));
+    }
+    bool reported_fallback = false;
+    for (; it != iter; it = std::next(it)) {
       ReportEvent(interactive_detector, it->Get(), presentation_timestamp);
+      if (tracing_enabled && !reported_fallback &&
+          it->Get()->GetEventTimingReportingInfo()->fallback_time.has_value()) {
+        TRACE_EVENT_INSTANT(
+            "devtools.timeline", "EventFallbackTime",
+            perfetto::Track::ThreadScoped(this),
+            it->Get()->GetEventTimingReportingInfo()->fallback_time.value(),
+            perfetto::Flow::ProcessScoped(presentation_index_to_report));
+        reported_fallback = true;
+      }
+    }
+    if (tracing_enabled) {
+      PerformanceEventTiming::EventTimingReportingInfo*
+          last_event_reporting_info =
+              std::prev(it)->Get()->GetEventTimingReportingInfo();
+      auto commit_finish_time = last_event_reporting_info->commit_finish_time;
+      if (commit_finish_time.is_null()) {
+        TRACE_EVENT_END("devtools.timeline",
+                        perfetto::Track::ThreadScoped(this),
+                        last_event_reporting_info->processing_end_time);
+      } else {
+        TRACE_EVENT_END("devtools.timeline",
+                        perfetto::Track::ThreadScoped(this),
+                        commit_finish_time);
+        TRACE_EVENT_INSTANT("devtools.timeline", "EventPresentation",
+                            perfetto::Track::ThreadScoped(this),
+                            last_event_reporting_info->presentation_time,
+                            perfetto::TerminatingFlow::ProcessScoped(
+                                presentation_index_to_report));
+      }
     }
     // Remove reported EventData objects.
     event_timing_entries_.erase(event_timing_entries_.begin(), iter);
@@ -820,6 +870,30 @@
             entry->GetEventTimingReportingInfo()->presentation_time);
     unsigned hash = WTF::GetHash(entry->name());
     WTF::AddFloatToHash(hash, entry->startTime());
+    auto track_id = perfetto::Track::ThreadScoped(this);
+    auto flow_id = perfetto::Flow::FromPointer(entry);
+    TRACE_EVENT_INSTANT("devtools.timeline", "EventCreation", track_id,
+                        entry->GetEventTimingReportingInfo()->creation_time,
+                        flow_id);
+    TRACE_EVENT_INSTANT(
+        "devtools.timeline", "EventEnqueuedToMainThread", track_id,
+        entry->GetEventTimingReportingInfo()->enqueued_to_main_thread_time,
+        flow_id);
+
+    TRACE_EVENT_BEGIN(
+        "devtools.timeline", "EventProcessing", track_id,
+        entry->GetEventTimingReportingInfo()->processing_start_time, flow_id,
+        [&](perfetto::EventContext ctx) {
+          auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
+          auto* data = event->set_event_timing();
+          entry->SetPerfettoData(DomWindow()->GetFrame(), data,
+                                 GetTimeOriginInternal());
+        });
+    TRACE_EVENT_END("devtools.timeline", track_id,
+                    entry->GetEventTimingReportingInfo()->processing_end_time);
+
+    // TODO(sullivan): Remove these events when DevTools migrates to the above
+    // perfetto events.
     TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
         "devtools.timeline", "EventTiming", hash, unsafe_start_time, "data",
         entry->ToTracedValue(DomWindow()->GetFrame()));
diff --git a/third_party/blink/renderer/modules/cache_storage/cache.cc b/third_party/blink/renderer/modules/cache_storage/cache.cc
index 22941ad11..6aa43ee 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache.cc
@@ -214,15 +214,19 @@
   }
 
   void FailedResponse() {
-    resolver_->RejectWithDOMException(
-        DOMExceptionCode::kNetworkError,
-        method_name_ + " encountered a network error");
+    if (resolver_->GetScriptState()->ContextIsValid()) {
+      resolver_->RejectWithDOMException(
+          DOMExceptionCode::kNetworkError,
+          method_name_ + " encountered a network error");
+    }
     Stop();
   }
 
   void AbortedResponse() {
-    resolver_->RejectWithDOMException(DOMExceptionCode::kAbortError,
-                                      method_name_ + " was aborted");
+    if (resolver_->GetScriptState()->ContextIsValid()) {
+      resolver_->RejectWithDOMException(DOMExceptionCode::kAbortError,
+                                        method_name_ + " was aborted");
+    }
     Stop();
   }
 
@@ -534,8 +538,8 @@
     // promise is never returned to script or chained to another handler.
     // If we return our real result and an exception occurs then unhandled
     // promise errors will occur.
-    ScriptValue rtn =
-        ScriptPromiseUntyped::CastUndefined(script_state).AsScriptValue();
+    ScriptValue rtn(script_state->GetIsolate(),
+                    ToResolvedUndefinedPromise(script_state).V8Promise());
 
     // If there is no loader, we were created as a reject handler.
     if (!response_loader_) {
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index c310600..4ca9301d 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -2338,6 +2338,22 @@
       CanvasPerformanceMonitor::DrawType::kImage);
 }
 
+// TODO(b/349835587): This is just a stub for now.
+void BaseRenderingContext2D::placeElement(Element* element,
+                                          double x,
+                                          double y,
+                                          ExceptionState& exception_state) {
+  HTMLCanvasElement* canvas = HostAsHTMLCanvasElement();
+  if (!element->IsDescendantOf(canvas)) {
+    exception_state.ThrowTypeError(
+        "Only elements that are part of the canvas fallback content subtree "
+        "(i.e. children of the <canvas> element) can be used with "
+        "placeElement().");
+  }
+
+  canvas->SetHasPlacedElements();
+}
+
 bool BaseRenderingContext2D::RectContainsTransformedRect(
     const gfx::RectF& rect,
     const SkIRect& transformed_rect) const {
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index db6126c5..d2099e2 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -250,7 +250,7 @@
   void placeElement(Element* element,
                     double x,
                     double y,
-                    ExceptionState& exception_state) {}
+                    ExceptionState& exception_state);
   void drawImage(const V8CanvasImageSource* image_source,
                  double x,
                  double y,
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_item.cc b/third_party/blink/renderer/modules/clipboard/clipboard_item.cc
index a43a4fd..eb87fd11 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_item.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_item.cc
@@ -23,7 +23,7 @@
     ExceptionState& exception_state) {
   // Check that incoming dictionary isn't empty. If it is, it's possible that
   // Javascript bindings implicitly converted an Object (like a
-  // ScriptPromiseUntyped) into {}, an empty dictionary.
+  // ScriptPromise<Blob>) into {}, an empty dictionary.
   if (!representations.size()) {
     exception_state.ThrowTypeError("Empty dictionary argument");
     return nullptr;
@@ -40,7 +40,11 @@
     if (web_custom_format.empty()) {
       // Any arbitrary type can be added to ClipboardItem, but there may not be
       // any read/write support for that type.
-      representations_.push_back(representation);
+      // TODO(caseq,japhet): we can't pass typed promises from bindings yet, but
+      // when we can, the type cast below should go away.
+      representations_.emplace_back(
+          representation.first,
+          static_cast<const ScriptPromise<Blob>&>(representation.second));
     } else {
       // Types with "web " prefix are special, so we do some level of MIME type
       // parsing here to get a valid web custom format type.
@@ -49,11 +53,14 @@
       // e.g. "web text/html" is a web custom MIME type & "text/html" is a
       // well-known MIME type. Removing the "web " prefix makes it hard to
       // differentiate between the two.
+      // TODO(caseq,japhet): we can't pass typed promises from bindings yet, but
+      // when we can, the type cast below should go away.
       String web_custom_format_string =
           String::Format("%s%s", ui::kWebClipboardFormatPrefix,
                          web_custom_format.Utf8().c_str());
-      representations_.emplace_back(web_custom_format_string,
-                                    representation.second);
+      representations_.emplace_back(
+          web_custom_format_string,
+          static_cast<const ScriptPromise<Blob>&>(representation.second));
       custom_format_types_.push_back(web_custom_format_string);
     }
   }
@@ -68,7 +75,7 @@
   return types;
 }
 
-ScriptPromiseUntyped ClipboardItem::getType(
+ScriptPromise<Blob> ClipboardItem::getType(
     ScriptState* script_state,
     const String& type,
     ExceptionState& exception_state) const {
@@ -79,7 +86,7 @@
 
   exception_state.ThrowDOMException(DOMExceptionCode::kNotFoundError,
                                     "The type was not found");
-  return ScriptPromiseUntyped();
+  return ScriptPromise<Blob>();
 }
 
 // static
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_item.h b/third_party/blink/renderer/modules/clipboard/clipboard_item.h
index 3e87815..9a5b0c4 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_item.h
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_item.h
@@ -11,6 +11,7 @@
 
 namespace blink {
 
+class Blob;
 class ScriptState;
 
 // A `ClipboardItem` holds data that was read from or will be written to the
@@ -48,17 +49,17 @@
   // retrieve. `exception_state`: The exception state to be updated if an error
   // occurs. Spec:
   // https://w3c.github.io/clipboard-apis/#dom-clipboarditem-gettype
-  ScriptPromiseUntyped getType(ScriptState* script_state,
-                               const String& type,
-                               ExceptionState& exception_state) const;
+  ScriptPromise<Blob> getType(ScriptState* script_state,
+                              const String& type,
+                              ExceptionState& exception_state) const;
 
   // Checks if a particular MIME type is supported by the Async Clipboard API.
   // `type` refers to a MIME type or a custom MIME type with a "web " prefix.
   // Spec: https://w3c.github.io/clipboard-apis/#dom-clipboarditem-supports
   static bool supports(const String& type);
 
-  const HeapVector<std::pair<String, ScriptPromiseUntyped>>&
-  GetRepresentations() const {
+  const HeapVector<std::pair<String, ScriptPromise<Blob>>>& GetRepresentations()
+      const {
     return representations_;
   }
 
@@ -70,7 +71,7 @@
 
  private:
   // Stores built-in and web custom MIME types.
-  HeapVector<std::pair<String, ScriptPromiseUntyped>> representations_;
+  HeapVector<std::pair<String, ScriptPromise<Blob>>> representations_;
   // The vector of custom MIME types that have a "web " prefix.
   Vector<String> custom_format_types_;
 };
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_item.idl b/third_party/blink/renderer/modules/clipboard/clipboard_item.idl
index a99bc64..b44083d4 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_item.idl
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_item.idl
@@ -11,10 +11,8 @@
   [RaisesException] constructor(record<DOMString, Promise<Blob>> items);
   readonly attribute FrozenArray<DOMString> types;
 
-  // The implementation returns an untyped promise
   [
     CallWith=ScriptState,
-    PromiseIDLTypeMismatch,
     RaisesException
   ] Promise<Blob> getType(DOMString type);
 
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_promise.h b/third_party/blink/renderer/modules/clipboard/clipboard_promise.h
index 70b98b7..476bb4173 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_promise.h
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_promise.h
@@ -168,7 +168,7 @@
   HeapVector<std::pair<String, Member<Blob>>> clipboard_item_data_;
   // The list of formats with their corresponding promises to the Blob data to
   // be written to the clipboard.
-  HeapVector<std::pair<String, ScriptPromiseUntyped>>
+  HeapVector<std::pair<String, ScriptPromise<Blob>>>
       clipboard_item_data_with_promises_;
   wtf_size_t clipboard_representation_index_ = 0;
   // List of custom format with "web " prefix.
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_script_transformer.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_script_transformer.cc
index d7fcfe9..08839769 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_script_transformer.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_script_transformer.cc
@@ -78,7 +78,12 @@
   writable_ = WritableStream::CreateWithCountQueueingStrategy(
       script_state, rtc_encoded_underlying_sink_,
       /*high_water_mark=*/1);
-  serialized_data_memory_accounter_.Register(SizeOfExternalMemoryInBytes());
+  serialized_data_memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                             SizeOfExternalMemoryInBytes());
+}
+
+RTCRtpScriptTransformer::~RTCRtpScriptTransformer() {
+  serialized_data_memory_accounter_.Clear(v8::Isolate::GetCurrent());
 }
 
 size_t RTCRtpScriptTransformer::SizeOfExternalMemoryInBytes() {
@@ -115,7 +120,7 @@
     // The data is put on the V8 GC heap here, and therefore the V8 GC does
     // the accounting from here on. We unregister the registered memory to
     // avoid double accounting.
-    serialized_data_memory_accounter_.Unregister();
+    serialized_data_memory_accounter_.Clear(isolate);
     value = data_as_serialized_script_value_->Deserialize(isolate, options);
   } else {
     value = v8::Null(isolate);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_script_transformer.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_script_transformer.h
index 6413973..b45bdac 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_script_transformer.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_script_transformer.h
@@ -9,7 +9,6 @@
 #include "base/thread_annotations.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
-#include "third_party/blink/renderer/bindings/core/v8/serialization/v8_external_memory_accounter.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/core/workers/custom_event_message.h"
@@ -18,6 +17,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_underlying_source_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/v8_external_memory_accounter.h"
 #include "third_party/blink/renderer/platform/heap/cross_thread_handle.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h"
@@ -33,7 +33,7 @@
 
  public:
   RTCRtpScriptTransformer() = default;
-  ~RTCRtpScriptTransformer() override = default;
+  ~RTCRtpScriptTransformer() override;
 
   explicit RTCRtpScriptTransformer(
       ScriptState* script_state,
diff --git a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
index 6451053..83e9a2559 100644
--- a/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/wait_until_observer.cc
@@ -79,7 +79,9 @@
           WTF::BindOnce(&WaitUntilObserver::OnPromiseRejected,
                         WrapPersistent(observer_.Get())));
       observer_ = nullptr;
-      return ScriptPromiseUntyped::Reject(script_state, value).AsScriptValue();
+      return ScriptValue(
+          script_state->GetIsolate(),
+          ScriptPromiseUntyped::Reject(script_state, value).V8Promise());
     }
 
     event_loop->EnqueueMicrotask(
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.idl b/third_party/blink/renderer/modules/webgpu/gpu_adapter.idl
index cdc2c7eb..a0a593a 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.idl
@@ -16,5 +16,5 @@
 
     [CallWith=ScriptState] Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
     // TODO(crbug.com/335383516): Remove this once synchronous info attribute is implemented.
-    [CallWith=ScriptState, Measure] Promise<GPUAdapterInfo> requestAdapterInfo();
+    [DeprecateAs=V8GPUAdapter_RequestAdapterInfo_Method, CallWith=ScriptState, Measure] Promise<GPUAdapterInfo> requestAdapterInfo();
 };
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index c530770c..9a44a4d1 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -527,6 +527,7 @@
     "bindings/v8_dom_activity_logger.h",
     "bindings/v8_dom_wrapper.cc",
     "bindings/v8_dom_wrapper.h",
+    "bindings/v8_external_memory_accounter.h",
     "bindings/v8_global_value_map.h",
     "bindings/v8_histogram_accumulator.cc",
     "bindings/v8_histogram_accumulator.h",
diff --git a/third_party/blink/renderer/platform/bindings/exception_state.cc b/third_party/blink/renderer/platform/bindings/exception_state.cc
index 9d80613..5fbd96e 100644
--- a/third_party/blink/renderer/platform/bindings/exception_state.cc
+++ b/third_party/blink/renderer/platform/bindings/exception_state.cc
@@ -173,19 +173,6 @@
   } else {
     DCHECK(isolate_);
     exception_.Reset(isolate_, exception);
-    // Temporary workaround while debugging the root cause of
-    // https://crbug.com/356158097. The workaround immediately materializes the
-    // stack trace string. The workaround is enabled by default, but can be
-    // disabled by experimental flag.
-    if (main_context_.GetClassName() &&
-        strcmp(main_context_.GetClassName(), "IDBDatabase") == 0) [[unlikely]] {
-      if (!RuntimeEnabledFeatures::
-              DOMExceptionV8CaptureStackTraceDisableWorkaroundEnabled())
-          [[likely]] {
-        std::ignore = exception.As<v8::Object>()->Get(
-            isolate_->GetCurrentContext(), V8String(isolate_, "stack"));
-      }
-    }
   }
 }
 
diff --git a/third_party/blink/renderer/platform/bindings/string_resource.h b/third_party/blink/renderer/platform/bindings/string_resource.h
index ad9f69e1..6c0e7c7 100644
--- a/third_party/blink/renderer/platform/bindings/string_resource.h
+++ b/third_party/blink/renderer/platform/bindings/string_resource.h
@@ -5,8 +5,10 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_STRING_RESOURCE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_STRING_RESOURCE_H_
 
+#include "base/compiler_specific.h"
 #include "base/dcheck_is_on.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string.h"
+#include "third_party/blink/renderer/platform/bindings/v8_external_memory_accounter.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -23,35 +25,43 @@
   explicit StringResourceBase(String string)
       : plain_string_(std::move(string)) {
     DCHECK(!plain_string_.IsNull());
-    v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
-        plain_string_.CharactersSizeInBytes());
+    memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                               plain_string_.CharactersSizeInBytes());
   }
 
   explicit StringResourceBase(AtomicString string)
       : atomic_string_(std::move(string)) {
     DCHECK(!atomic_string_.IsNull());
-    v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
-        atomic_string_.CharactersSizeInBytes());
+    memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                               atomic_string_.CharactersSizeInBytes());
   }
 
   explicit StringResourceBase(ParkableString string)
       : parkable_string_(string) {
     // TODO(lizeb): This is only true without compression.
     DCHECK(!parkable_string_.IsNull());
-    v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
-        parkable_string_.CharactersSizeInBytes());
+    memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                               parkable_string_.CharactersSizeInBytes());
   }
 
   StringResourceBase(const StringResourceBase&) = delete;
   StringResourceBase& operator=(const StringResourceBase&) = delete;
 
   virtual ~StringResourceBase() {
-    int64_t reduced_external_memory = plain_string_.CharactersSizeInBytes();
-    if (plain_string_.Impl() != atomic_string_.Impl() &&
-        !atomic_string_.IsNull())
-      reduced_external_memory += atomic_string_.CharactersSizeInBytes();
-    v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
-        -reduced_external_memory);
+    int64_t reduced_external_memory = 0;
+    if (!parkable_string_.IsNull()) {
+      DCHECK(plain_string_.IsNull());
+      DCHECK(atomic_string_.IsNull());
+      reduced_external_memory = parkable_string_.CharactersSizeInBytes();
+    } else {
+      reduced_external_memory = plain_string_.CharactersSizeInBytes();
+      if (plain_string_.Impl() != atomic_string_.Impl() &&
+          !atomic_string_.IsNull()) {
+        reduced_external_memory += atomic_string_.CharactersSizeInBytes();
+      }
+    }
+    memory_accounter_.Decrease(v8::Isolate::GetCurrent(),
+                               reduced_external_memory);
   }
 
   String GetWTFString() {
@@ -73,8 +83,8 @@
       atomic_string_ = AtomicString(plain_string_);
       DCHECK(!atomic_string_.IsNull());
       if (plain_string_.Impl() != atomic_string_.Impl()) {
-        v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
-            atomic_string_.CharactersSizeInBytes());
+        memory_accounter_.Increase(v8::Isolate::GetCurrent(),
+                                   atomic_string_.CharactersSizeInBytes());
       }
     }
     return atomic_string_;
@@ -117,6 +127,8 @@
   // If this string is parkable, its value is held here, and the other
   // members above are null.
   ParkableString parkable_string_;
+
+  NO_UNIQUE_ADDRESS V8ExternalMemoryAccounterBase memory_accounter_;
 };
 
 // Even though StringResource{8,16}Base are effectively empty in release mode,
diff --git a/third_party/blink/renderer/platform/bindings/v8_external_memory_accounter.h b/third_party/blink/renderer/platform/bindings/v8_external_memory_accounter.h
new file mode 100644
index 0000000..7e538d1f
--- /dev/null
+++ b/third_party/blink/renderer/platform/bindings/v8_external_memory_accounter.h
@@ -0,0 +1,70 @@
+// 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_PLATFORM_BINDINGS_V8_EXTERNAL_MEMORY_ACCOUNTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_EXTERNAL_MEMORY_ACCOUNTER_H_
+
+#include <stdlib.h>
+
+#include "base/check_op.h"
+#include "base/memory/raw_ptr.h"
+#include "v8/include/v8-isolate.h"
+
+namespace blink {
+
+class V8ExternalMemoryAccounterBase {
+ public:
+  ~V8ExternalMemoryAccounterBase() {
+#if DCHECK_IS_ON()
+    DCHECK_EQ(amount_of_external_memory_, 0U);
+#endif
+  }
+
+  void Increase(v8::Isolate* isolate, size_t size) {
+#if DCHECK_IS_ON()
+    DCHECK(isolate == isolate_ || isolate_ == nullptr);
+    isolate_ = isolate;
+    amount_of_external_memory_ += size;
+#endif
+    isolate->AdjustAmountOfExternalAllocatedMemory(static_cast<int64_t>(size));
+  }
+
+  void Decrease(v8::Isolate* isolate, size_t size) {
+#if DCHECK_IS_ON()
+    DCHECK_EQ(isolate, isolate_);
+    DCHECK_GE(amount_of_external_memory_, size);
+    amount_of_external_memory_ -= size;
+#endif
+    isolate->AdjustAmountOfExternalAllocatedMemory(-static_cast<int64_t>(size));
+  }
+
+ private:
+#if DCHECK_IS_ON()
+  size_t amount_of_external_memory_ = 0;
+  raw_ptr<v8::Isolate> isolate_;
+#endif
+};
+
+class V8ExternalMemoryAccounter {
+ public:
+  void Increase(v8::Isolate* isolate, size_t size) {
+    amount_of_external_memory_ += size;
+    memory_accounter_base_.Increase(isolate, size);
+  }
+
+  void Clear(v8::Isolate* isolate) {
+    if (amount_of_external_memory_ != 0) {
+      memory_accounter_base_.Decrease(isolate, amount_of_external_memory_);
+    }
+    amount_of_external_memory_ = 0;
+  }
+
+ private:
+  NO_UNIQUE_ADDRESS V8ExternalMemoryAccounterBase memory_accounter_base_;
+  size_t amount_of_external_memory_ = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_EXTERNAL_MEMORY_ACCOUNTER_H_
diff --git a/third_party/blink/renderer/platform/heap/collection_support/heap_vector.h b/third_party/blink/renderer/platform/heap/collection_support/heap_vector.h
index 418b5341..c37043f5 100644
--- a/third_party/blink/renderer/platform/heap/collection_support/heap_vector.h
+++ b/third_party/blink/renderer/platform/heap/collection_support/heap_vector.h
@@ -17,14 +17,12 @@
 
 namespace blink {
 
-// TODO(crbug.com/355003172): The default value of checked_iter should be true.
-template <typename T, wtf_size_t inlineCapacity = 0, bool checked_iter = false>
-class HeapVector final
-    : public GarbageCollected<HeapVector<T, inlineCapacity, checked_iter>>,
-      public Vector<T, inlineCapacity, HeapAllocator, checked_iter> {
+template <typename T, wtf_size_t inlineCapacity = 0>
+class HeapVector final : public GarbageCollected<HeapVector<T, inlineCapacity>>,
+                         public Vector<T, inlineCapacity, HeapAllocator> {
   DISALLOW_NEW();
 
-  using BaseVector = Vector<T, inlineCapacity, HeapAllocator, checked_iter>;
+  using BaseVector = Vector<T, inlineCapacity, HeapAllocator>;
 
  public:
   HeapVector() = default;
@@ -34,7 +32,7 @@
   HeapVector(wtf_size_t size, const T& val) : BaseVector(size, val) {}
 
   template <wtf_size_t otherCapacity>
-  HeapVector(const HeapVector<T, otherCapacity, checked_iter>& other)  // NOLINT
+  HeapVector(const HeapVector<T, otherCapacity>& other)  // NOLINT
       : BaseVector(other) {}
 
   HeapVector(const HeapVector& other)
@@ -53,7 +51,7 @@
       typename Proj,
       typename = std::enable_if_t<
           std::is_invocable_v<Proj, typename BaseVector::const_reference>>>
-  HeapVector(const HeapVector<U, otherSize, checked_iter>& other, Proj proj)
+  HeapVector(const HeapVector<U, otherSize>& other, Proj proj)
       : BaseVector(static_cast<const BaseVector&>(other), std::move(proj)) {}
 
   template <typename Collection,
@@ -101,9 +99,8 @@
 template <typename T>
 concept IsHeapVector = requires { typename HeapVector<T>; };
 
-template <typename T, wtf_size_t inlineCapacity, bool checked_iter>
-constexpr HeapVector<T, inlineCapacity, checked_iter>::TypeConstraints::
-    TypeConstraints() {
+template <typename T, wtf_size_t inlineCapacity>
+constexpr HeapVector<T, inlineCapacity>::TypeConstraints::TypeConstraints() {
   static_assert(std::is_trivially_destructible_v<HeapVector> || inlineCapacity,
                 "HeapVector must be trivially destructible.");
   static_assert(!WTF::IsWeak<T>::value,
diff --git a/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc b/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc
index 63057ad..23c884fb 100644
--- a/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc
+++ b/third_party/blink/renderer/platform/media/multi_buffer_data_source_unittest.cc
@@ -130,8 +130,9 @@
   TestUrlData(const GURL& url,
               CorsMode cors_mode,
               UrlIndex* url_index,
+              UrlData::CacheMode cache_mode,
               scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-      : UrlData(url, cors_mode, url_index, task_runner),
+      : UrlData(url, cors_mode, url_index, cache_mode, task_runner),
         block_shift_(url_index->block_shift()),
         task_runner_(std::move(task_runner)) {}
 
@@ -167,9 +168,11 @@
         task_runner_(std::move(task_runner)) {}
 
   scoped_refptr<UrlData> NewUrlData(const GURL& url,
-                                    UrlData::CorsMode cors_mode) override {
-    last_url_data_ =
-        base::MakeRefCounted<TestUrlData>(url, cors_mode, this, task_runner_);
+                                    UrlData::CorsMode cors_mode,
+                                    UrlData::CacheMode cache_mode) override {
+    NotifyNewUrlData(url, cors_mode, cache_mode);
+    last_url_data_ = base::MakeRefCounted<TestUrlData>(
+        url, cors_mode, this, cache_mode, task_runner_);
     return last_url_data_;
   }
 
@@ -178,6 +181,9 @@
     return last_url_data_;
   }
 
+  MOCK_METHOD3(NotifyNewUrlData,
+               void(GURL, UrlData::CorsMode, UrlData::CacheMode));
+
  private:
   scoped_refptr<TestUrlData> last_url_data_;
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
@@ -256,7 +262,7 @@
     GURL gurl(url);
     media_log_ = std::make_unique<NiceMock<media::MockMediaLog>>();
     data_source_ = std::make_unique<MockMultiBufferDataSource>(
-        task_runner_, url_index_.GetByUrl(gurl, cors_mode, UrlIndex::kNormal),
+        task_runner_, url_index_.GetByUrl(gurl, cors_mode, UrlData::kNormal),
         media_log_.get(), &host_);
     data_source_->SetPreload(preload_);
 
@@ -1031,7 +1037,7 @@
   MockMultiBufferDataSource source2(
       task_runner_,
       url_index_.GetByUrl(GURL(kHttpUrl), UrlData::CORS_UNSPECIFIED,
-                          UrlIndex::kNormal),
+                          UrlData::kNormal),
       media_log_.get(), &host2);
   source2.SetPreload(preload_);
 
@@ -1440,7 +1446,7 @@
   media_log_ = std::make_unique<NiceMock<media::MockMediaLog>>();
   data_source_ = std::make_unique<MockMultiBufferDataSource>(
       task_runner_,
-      url_index_.GetByUrl(gurl, UrlData::CORS_UNSPECIFIED, UrlIndex::kNormal),
+      url_index_.GetByUrl(gurl, UrlData::CORS_UNSPECIFIED, UrlData::kNormal),
       media_log_.get(), &host_);
   data_source_->SetPreload(preload_);
 
@@ -1552,6 +1558,112 @@
   Stop();
 }
 
+TEST_F(MultiBufferDataSourceTest, PreserveCachingModeAfterRedirect) {
+  GURL start = GURL("https://start.com");
+  GURL redir = GURL("https://redir.com");
+  media_log_ = std::make_unique<NiceMock<media::MockMediaLog>>();
+  WebURL url{redir};
+  WebURLResponse redirect_response(start);
+  redirect_response.SetHttpStatusCode(307);
+  WebURLResponse data_response(redir);
+  data_response.SetHttpStatusCode(200);
+  data_response.SetExpectedContentLength(kDataSize);
+  data_response.SetHttpHeaderField(WebString::FromUTF8("Accept-Ranges"),
+                                   WebString::FromUTF8("bytes"));
+
+  // Create a data source for a url which redirects. This will create a new
+  // UrlData that bypasses any cache lookups (but can still be added to the
+  // cache). This will create a new UrlData object with the bypass flag set.
+  // The redirection will create another UrlData object with the new url, which
+  // will also be in bypass mode.
+  {
+    EXPECT_CALL(url_index_,
+                NotifyNewUrlData(start, _, UrlData::kCacheDisabled));
+    auto data_source = std::make_unique<MockMultiBufferDataSource>(
+        task_runner_,
+        url_index_.GetByUrl(start, UrlData::CORS_UNSPECIFIED,
+                            UrlData::kCacheDisabled),
+        media_log_.get(), &host_);
+    data_source->SetPreload(preload_);
+    auto response_generator =
+        std::make_unique<TestResponseGenerator>(start, kFileSize);
+    data_source->SetIsClientAudioElement(false);
+    EXPECT_CALL(*this, OnInitialize(true));
+    data_source->Initialize(base::BindOnce(
+        &MultiBufferDataSourceTest::OnInitialize, base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(data_source->downloading(), false);
+    EXPECT_CALL(url_index_,
+                NotifyNewUrlData(redir, _, UrlData::kCacheDisabled));
+    data_provider()->WillFollowRedirect(url, redirect_response);
+    EXPECT_CALL(host_, AddBufferedByteRange(0, _));
+    EXPECT_CALL(host_, SetTotalBytes(kDataSize));
+    Respond(data_response);
+    ReceiveData(kDataSize);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Make another data source for the same URL, but this time, make it in normal
+  // cache mode. This will still create a new UrlData for the initial URL
+  // because the redirect was temporary (307). The redirect will NOT create a
+  // new UrlData however, because that one is cached, as the previous response
+  // was a 200.
+  {
+    EXPECT_CALL(url_index_, NotifyNewUrlData(start, _, UrlData::kNormal));
+    auto data_source = std::make_unique<MockMultiBufferDataSource>(
+        task_runner_,
+        url_index_.GetByUrl(start, UrlData::CORS_UNSPECIFIED, UrlData::kNormal),
+        media_log_.get(), &host_);
+    data_source->SetPreload(preload_);
+    auto response_generator =
+        std::make_unique<TestResponseGenerator>(start, kFileSize);
+    data_source->SetIsClientAudioElement(false);
+    EXPECT_CALL(*this, OnInitialize(true));
+    data_source->Initialize(base::BindOnce(
+        &MultiBufferDataSourceTest::OnInitialize, base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(data_source->downloading(), false);
+    EXPECT_CALL(url_index_, NotifyNewUrlData(redir, _, _)).Times(0);
+    data_provider()->WillFollowRedirect(url, redirect_response);
+    EXPECT_CALL(host_, AddBufferedByteRange(0, _));
+    EXPECT_CALL(host_, SetTotalBytes(kDataSize));
+    Respond(data_response);
+    ReceiveData(kDataSize);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Make another data source for the same URL, but again in bypass cache lookup
+  // mode. This will create another UrlData object for the first URL, but then
+  // will bypass the cached data and create another new UrlData object for the
+  // redirection url.
+  {
+    EXPECT_CALL(url_index_,
+                NotifyNewUrlData(start, _, UrlData::kCacheDisabled));
+    auto data_source = std::make_unique<MockMultiBufferDataSource>(
+        task_runner_,
+        url_index_.GetByUrl(start, UrlData::CORS_UNSPECIFIED,
+                            UrlData::kCacheDisabled),
+        media_log_.get(), &host_);
+    data_source->SetPreload(preload_);
+    auto response_generator =
+        std::make_unique<TestResponseGenerator>(start, kFileSize);
+    data_source->SetIsClientAudioElement(false);
+    EXPECT_CALL(*this, OnInitialize(true));
+    data_source->Initialize(base::BindOnce(
+        &MultiBufferDataSourceTest::OnInitialize, base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(data_source->downloading(), false);
+    EXPECT_CALL(url_index_,
+                NotifyNewUrlData(redir, _, UrlData::kCacheDisabled));
+    data_provider()->WillFollowRedirect(url, redirect_response);
+    EXPECT_CALL(host_, AddBufferedByteRange(0, _));
+    EXPECT_CALL(host_, SetTotalBytes(kDataSize));
+    Respond(data_response);
+    ReceiveData(kDataSize);
+    base::RunLoop().RunUntilIdle();
+  }
+}
+
 TEST_F(MultiBufferDataSourceTest, LengthKnownAtEOF) {
   Initialize(kHttpUrl, true);
   // Server responds without content-length.
@@ -1820,7 +1932,7 @@
   media_log_ = std::make_unique<NiceMock<media::MockMediaLog>>();
   data_source_ = std::make_unique<MockMultiBufferDataSource>(
       task_runner_,
-      url_index_.GetByUrl(gurl, UrlData::CORS_UNSPECIFIED, UrlIndex::kNormal),
+      url_index_.GetByUrl(gurl, UrlData::CORS_UNSPECIFIED, UrlData::kNormal),
       media_log_.get(), &host_);
   data_source_->SetPreload(preload_);
 
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
index 3d6913f..1dfe355 100644
--- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
+++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider.cc
@@ -227,7 +227,7 @@
 
   if (!redirects_to_.is_empty()) {
     destination_url_data = url_data_->url_index()->GetByUrl(
-        redirects_to_, cors_mode_, UrlIndex::kNormal);
+        redirects_to_, cors_mode_, url_data_->cache_lookup_mode());
     redirects_to_ = GURL();
   }
 
@@ -244,8 +244,8 @@
   destination_url_data->set_valid_until(base::Time::Now() +
                                         GetCacheValidUntil(response));
 
-  destination_url_data->set_cacheable(GetReasonsForUncacheability(response) ==
-                                      0);
+  bool cacheable = GetReasonsForUncacheability(response) == 0;
+  destination_url_data->set_cacheable(cacheable);
 
   // Expected content length can be |kPositionNotSpecified|, in that case
   // |content_length_| is not specified and this is a streaming response.
diff --git a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
index 8ece4de..e49137f 100644
--- a/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
+++ b/third_party/blink/renderer/platform/media/resource_multi_buffer_data_provider_unittest.cc
@@ -86,8 +86,8 @@
 
   void Initialize(const char* url, int first_position) {
     gurl_ = GURL(url);
-    url_data_ = url_index_.GetByUrl(gurl_, UrlData::CORS_UNSPECIFIED,
-                                    UrlIndex::kNormal);
+    url_data_ =
+        url_index_.GetByUrl(gurl_, UrlData::CORS_UNSPECIFIED, UrlData::kNormal);
     url_data_->set_etag(kEtag);
     DCHECK(url_data_);
     url_data_->OnRedirect(
diff --git a/third_party/blink/renderer/platform/media/url_index.cc b/third_party/blink/renderer/platform/media/url_index.cc
index 9d18fc0a..f9d3822e 100644
--- a/third_party/blink/renderer/platform/media/url_index.cc
+++ b/third_party/blink/renderer/platform/media/url_index.cc
@@ -52,12 +52,18 @@
                  const GURL& url,
                  CorsMode cors_mode,
                  UrlIndex* url_index,
+                 CacheMode cache_lookup_mode,
                  scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : UrlData(url, cors_mode, url_index, std::move(task_runner)) {}
+    : UrlData(url,
+              cors_mode,
+              url_index,
+              cache_lookup_mode,
+              std::move(task_runner)) {}
 
 UrlData::UrlData(const GURL& url,
                  CorsMode cors_mode,
                  UrlIndex* url_index,
+                 CacheMode cache_lookup_mode,
                  scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : url_(url),
       have_data_origin_(false),
@@ -67,6 +73,7 @@
       length_(kPositionNotSpecified),
       range_supported_(false),
       cacheable_(false),
+      cache_lookup_mode_(cache_lookup_mode),
       multibuffer_(this, url_index_->block_shift_, std::move(task_runner)) {}
 
 UrlData::~UrlData() = default;
@@ -91,6 +98,7 @@
     // set_length() will not override the length if already known.
     set_length(other->length_);
     cacheable_ |= other->cacheable_;
+    cache_lookup_mode_ = other->cache_lookup_mode_;
     range_supported_ |= other->range_supported_;
     if (last_modified_.is_null()) {
       last_modified_ = other->last_modified_;
@@ -266,21 +274,24 @@
 
 scoped_refptr<UrlData> UrlIndex::GetByUrl(const GURL& gurl,
                                           UrlData::CorsMode cors_mode,
-                                          CacheMode cache_mode) {
-  if (cache_mode == kNormal) {
+                                          UrlData::CacheMode cache_mode) {
+  if (cache_mode == UrlData::kNormal) {
     auto i = indexed_data_.find(std::make_pair(gurl, cors_mode));
     if (i != indexed_data_.end() && i->second->Valid()) {
       return i->second;
     }
   }
 
-  return NewUrlData(gurl, cors_mode);
+  return NewUrlData(gurl, cors_mode, cache_mode);
 }
 
-scoped_refptr<UrlData> UrlIndex::NewUrlData(const GURL& url,
-                                            UrlData::CorsMode cors_mode) {
+scoped_refptr<UrlData> UrlIndex::NewUrlData(
+    const GURL& url,
+    UrlData::CorsMode cors_mode,
+    UrlData::CacheMode cache_lookup_mode) {
   return base::MakeRefCounted<UrlData>(base::PassKey<UrlIndex>(), url,
-                                       cors_mode, this, task_runner_);
+                                       cors_mode, this, cache_lookup_mode,
+                                       task_runner_);
 }
 
 void UrlIndex::OnMemoryPressure(
@@ -343,6 +354,11 @@
     return url_data;
   }
 
+  // If the url data should bypass the cache lookup, we want to not merge it.
+  if (url_data->cache_lookup_mode() == UrlData::kCacheDisabled) {
+    return url_data;
+  }
+
   if (url_data->Valid()) {
     if ((!iter->second->Valid() ||
          url_data->CachedSize() > iter->second->CachedSize())) {
diff --git a/third_party/blink/renderer/platform/media/url_index.h b/third_party/blink/renderer/platform/media/url_index.h
index b87b0041..e6062fa7 100644
--- a/third_party/blink/renderer/platform/media/url_index.h
+++ b/third_party/blink/renderer/platform/media/url_index.h
@@ -70,12 +70,14 @@
  public:
   // Keep in sync with WebMediaPlayer::CorsMode.
   enum CorsMode { CORS_UNSPECIFIED, CORS_ANONYMOUS, CORS_USE_CREDENTIALS };
+  enum CacheMode { kNormal, kCacheDisabled };
   using KeyType = std::pair<GURL, CorsMode>;
 
   UrlData(base::PassKey<UrlIndex>,
           const GURL& url,
           CorsMode cors_mode,
           UrlIndex* url_index,
+          CacheMode cache_lookup_mode,
           scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   UrlData(const UrlData&) = delete;
   UrlData& operator=(const UrlData&) = delete;
@@ -101,6 +103,10 @@
   // HTTP disk cache.
   bool cacheable() const { return cacheable_; }
 
+  // True if this UrlData and any it might redirect to should bypass cache
+  // lookups, regardless of disk cache or response status.
+  CacheMode cache_lookup_mode() const { return cache_lookup_mode_; }
+
   // Last used time.
   base::Time last_used() const { return last_used_; }
 
@@ -183,6 +189,7 @@
   UrlData(const GURL& url,
           CorsMode cors_mode,
           UrlIndex* url_index,
+          CacheMode cache_lookup_mode,
           scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   virtual ~UrlData();
 
@@ -228,6 +235,11 @@
   // will not cache this url.
   bool cacheable_;
 
+  // While `cacheable_` determines whether this UrlData's underlying data should
+  // be stored in the cache, `cache_lookup_mode_` determines whether this
+  // UrlData should use existing underlying cached data.
+  CacheMode cache_lookup_mode_;
+
   // https://html.spec.whatwg.org/#cors-cross-origin
   bool is_cors_cross_origin_ = false;
 
@@ -265,8 +277,6 @@
            scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   virtual ~UrlIndex();
 
-  enum CacheMode { kNormal, kCacheDisabled };
-
   // Look up an UrlData in the index and return it. If none is found,
   // create a new one. Note that newly created UrlData entries are NOT
   // added to the index, instead you must call TryInsert on them after
@@ -276,7 +286,7 @@
   // released before |this| is destroyed.
   scoped_refptr<UrlData> GetByUrl(const GURL& gurl,
                                   UrlData::CorsMode cors_mode,
-                                  CacheMode cache_mode);
+                                  UrlData::CacheMode cache_mode);
 
   // Add the given UrlData to the index if possible. If a better UrlData
   // is already present in the index, return it instead. (If not, we just
@@ -309,8 +319,10 @@
   void RemoveUrlData(const scoped_refptr<UrlData>& url_data);
 
   // Virtual so we can override it in tests.
-  virtual scoped_refptr<UrlData> NewUrlData(const GURL& url,
-                                            UrlData::CorsMode cors_mode);
+  virtual scoped_refptr<UrlData> NewUrlData(
+      const GURL& url,
+      UrlData::CorsMode cors_mode,
+      UrlData::CacheMode cache_lookup_mode);
 
   void OnMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
diff --git a/third_party/blink/renderer/platform/media/url_index_unittest.cc b/third_party/blink/renderer/platform/media/url_index_unittest.cc
index a31a0303..49a8bc7 100644
--- a/third_party/blink/renderer/platform/media/url_index_unittest.cc
+++ b/third_party/blink/renderer/platform/media/url_index_unittest.cc
@@ -26,7 +26,7 @@
   scoped_refptr<UrlData> GetByUrl(const GURL& gurl,
                                   UrlData::CorsMode cors_mode) {
     scoped_refptr<UrlData> ret =
-        url_index_.GetByUrl(gurl, cors_mode, UrlIndex::kNormal);
+        url_index_.GetByUrl(gurl, cors_mode, UrlData::kNormal);
     EXPECT_EQ(ret->url(), gurl);
     EXPECT_EQ(ret->cors_mode(), cors_mode);
     return ret;
@@ -166,14 +166,14 @@
   UrlData::CorsMode cors = UrlData::CORS_UNSPECIFIED;
 
   scoped_refptr<UrlData> url_data =
-      url_index_.GetByUrl(url, cors, UrlIndex::kNormal);
+      url_index_.GetByUrl(url, cors, UrlData::kNormal);
   url_data->Use();
   url_data->set_range_supported();
   EXPECT_TRUE(url_data->Valid());
   url_index_.TryInsert(url_data);
 
-  EXPECT_EQ(url_data, url_index_.GetByUrl(url, cors, UrlIndex::kNormal));
-  EXPECT_NE(url_data, url_index_.GetByUrl(url, cors, UrlIndex::kCacheDisabled));
+  EXPECT_EQ(url_data, url_index_.GetByUrl(url, cors, UrlData::kNormal));
+  EXPECT_NE(url_data, url_index_.GetByUrl(url, cors, UrlData::kCacheDisabled));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
index da236d8..39fd85d 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -910,7 +910,7 @@
       main_task_runner_,
       url_index_->GetByUrl(
           url, static_cast<UrlData::CorsMode>(cors_mode),
-          is_cache_disabled ? UrlIndex::kCacheDisabled : UrlIndex::kNormal),
+          is_cache_disabled ? UrlData::kCacheDisabled : UrlData::kNormal),
       media_log_.get(), buffered_data_source_host_.get(),
       base::BindRepeating(&WebMediaPlayerImpl::NotifyDownloading, weak_this_));
 
@@ -1626,23 +1626,11 @@
 }
 
 #if BUILDFLAG(ENABLE_FFMPEG)
-void WebMediaPlayerImpl::AddAudioTrack(const std::string& id,
-                                       const std::string& label,
-                                       const std::string& language,
-                                       bool is_first_track) {
-  client_->AddMediaTrack(media::MediaTrack::CreateAudioTrack(
-      id, media::MediaTrack::AudioKind::kMain, label, language,
-      is_first_track));
+
+void WebMediaPlayerImpl::AddMediaTrack(const media::MediaTrack& track) {
+  client_->AddMediaTrack(track);
 }
 
-void WebMediaPlayerImpl::AddVideoTrack(const std::string& id,
-                                       const std::string& label,
-                                       const std::string& language,
-                                       bool is_first_track) {
-  client_->AddMediaTrack(media::MediaTrack::CreateVideoTrack(
-      id, media::MediaTrack::VideoKind::kMain, label, language,
-      is_first_track));
-}
 #endif  // BUILDFLAG(ENABLE_FFMPEG)
 
 #if BUILDFLAG(ENABLE_HLS_DEMUXER)
@@ -1653,7 +1641,7 @@
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   auto url_data = url_index_->GetByUrl(
       gurl, static_cast<UrlData::CorsMode>(cors_mode_),
-      is_cache_disabled_ ? UrlIndex::kCacheDisabled : UrlIndex::kNormal);
+      is_cache_disabled_ ? UrlData::kCacheDisabled : UrlData::kNormal);
   std::move(cb).Run(std::move(url_data));
 }
 
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.h b/third_party/blink/renderer/platform/media/web_media_player_impl.h
index e18b7db..7fe4a15 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.h
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.h
@@ -449,14 +449,7 @@
   void DemuxerRequestsSeek(base::TimeDelta seek_time) override;
 
 #if BUILDFLAG(ENABLE_FFMPEG)
-  void AddAudioTrack(const std::string& id,
-                     const std::string& label,
-                     const std::string& language,
-                     bool is_first_track) override;
-  void AddVideoTrack(const std::string& id,
-                     const std::string& label,
-                     const std::string& language,
-                     bool is_first_track) override;
+  void AddMediaTrack(const media::MediaTrack&) override;
 #endif  // BUILDFLAG(ENABLE_FFMPEG)
 
 #if BUILDFLAG(ENABLE_HLS_DEMUXER)
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 596f32f0..69aaa21 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1111,6 +1111,12 @@
       status: "experimental",
     },
     {
+      // For ::column pseudo element for fragment styling.
+      // https://github.com/flackr/carousel/blob/main/fragmentation/README.md
+      name: "CSSPseudoColumn",
+      status: "experimental",
+    },
+    {
       // Enables the :open and :closed pseudo-selectors.
       // https://chromestatus.com/feature/5085419215781888
       name: "CSSPseudoOpenClosed",
@@ -1150,6 +1156,11 @@
       status: "stable",
     },
     {
+      // crbug.com/359616070
+      name: "CSSRelativeColorLateResolveAlways",
+      status: "stable",
+    },
+    {
       // crbug.com/325309578
       name: "CSSRelativeColorSupportsCurrentcolor",
       status: "experimental",
@@ -1525,6 +1536,12 @@
       status: "experimental",
     },
     // Enables the ability to use Document Policy header to control feature
+    // ExpectNoEmbeddedResources.
+    {
+      name: "DocumentPolicyExpectNoEmbeddedResources",
+      status: "experimental",
+    },
+    // Enables the ability to use Document Policy header to control feature
     // IncludeJSCallStacksInCrashReports. https://chromestatus.com/feature/4731248572628992
     {
       name: "DocumentPolicyIncludeJSCallStacksInCrashReports",
@@ -1554,12 +1571,6 @@
     {
       name: "DocumentWrite",
     },
-    // When this flag is enabled, a workaround for https://crbug.com/356158097
-    // is removed.
-    {
-      name: "DOMExceptionV8CaptureStackTraceDisableWorkaround",
-      status: "experimental"
-    },
     {
       name: "DOMPartsAPI",
       status: "experimental",
@@ -2676,6 +2687,12 @@
       name: "MeterAppearanceNoneFallbackStyle",
       status: "stable",
     },
+    {
+      // If enabled, the <meter> widget appearance will devolve based on user
+      // declared styles as defined in the css-ui-4 spec
+      name: "MeterDevolveAppearance",
+      status: "stable",
+    },
     // This is enabled by default on Windows only. The only part that's
     // "experimental" is the support on other platforms.
     {
@@ -2734,8 +2751,9 @@
       status: "test",
     },
     {
+      // https://chromestatus.com/feature/6270155647352832
       name: "MultiSmoothScrollIntoView",
-      status: "experimental",
+      status: "stable",
     },
     // crbug.com/1446498: This feature is being used for the deprecation of
     // Mutation Events.
@@ -3517,6 +3535,12 @@
       status: "stable",
     },
     {
+      // When enabled, the root element will have a placeholder after all text is deleted.
+      // crbug.com/40702380
+      name: "RootElementWithPlaceHolderAfterDeletingSelection",
+      status: "stable",
+    },
+    {
       name: "RtcAudioJitterBufferMaxPackets",
       origin_trial_feature_name: "RtcAudioJitterBufferMaxPackets",
       status: "experimental",
diff --git a/third_party/blink/renderer/platform/wtf/forward.h b/third_party/blink/renderer/platform/wtf/forward.h
index 2d9b56c2..b3b1e7a0 100644
--- a/third_party/blink/renderer/platform/wtf/forward.h
+++ b/third_party/blink/renderer/platform/wtf/forward.h
@@ -34,11 +34,9 @@
 template <typename T>
 class StringBuffer;
 class PartitionAllocator;
-// TODO(crbug.com/355003172): The default value of checked_iter should be true.
 template <typename T,
           wtf_size_t inlineCapacity = 0,
-          typename Allocator = PartitionAllocator,
-          bool checked_iter = false>
+          typename Allocator = PartitionAllocator>
 class Vector;
 
 class AtomicString;
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index 7c3fa57..50b6b57 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -1065,19 +1065,6 @@
   T* current_ = nullptr;
 };
 
-template <typename T, bool checked_iter>
-struct IteratorSelector;
-
-template <typename T>
-struct IteratorSelector<T, true> {
-  using Type = base::CheckedContiguousIterator<T>;
-};
-
-template <typename T>
-struct IteratorSelector<T, false> {
-  using Type = UncheckedIterator<T>;
-};
-
 //
 // Vector
 //
@@ -1205,10 +1192,7 @@
 template <typename T, wtf_size_t InlineCapacity>
 inline constexpr bool kVectorNeedsDestructor<T, InlineCapacity, true> = true;
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 class Vector : private VectorBuffer<T, INLINE_CAPACITY, Allocator> {
   USE_ALLOCATOR(Vector, Allocator);
   using Base = VectorBuffer<T, INLINE_CAPACITY, Allocator>;
@@ -1224,8 +1208,10 @@
   using pointer = value_type*;
   using const_pointer = const value_type*;
 
-  using iterator = typename IteratorSelector<T, checked_iter>::Type;
-  using const_iterator = typename IteratorSelector<const T, checked_iter>::Type;
+  // TODO(crbug.com/355003172): We should try using
+  // base::CheckedContiguousIterator instead of UncheckedIterator.
+  using iterator = UncheckedIterator<T>;
+  using const_iterator = UncheckedIterator<const T>;
   using reverse_iterator = std::reverse_iterator<iterator>;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
@@ -1249,11 +1235,11 @@
   // Copying.
   Vector(const Vector&);
   template <wtf_size_t otherCapacity>
-  explicit Vector(const Vector<T, otherCapacity, Allocator, checked_iter>&);
+  explicit Vector(const Vector<T, otherCapacity, Allocator>&);
 
   Vector& operator=(const Vector&);
   template <wtf_size_t otherCapacity>
-  Vector& operator=(const Vector<T, otherCapacity, Allocator, checked_iter>&);
+  Vector& operator=(const Vector<T, otherCapacity, Allocator>&);
 
   // Copying with projection.
   template <
@@ -1329,36 +1315,10 @@
   const T* data() const { return Base::Buffer(); }
 
   // Iterators and reverse iterators. They are invalidated on a reallocation.
-  iterator begin() {
-    if constexpr (checked_iter) {
-      return iterator(data(), DataEnd());
-    } else {
-      return iterator(data());
-    }
-  }
-  iterator end() {
-    if constexpr (checked_iter) {
-      auto* e = DataEnd();
-      return iterator(data(), e, e);
-    } else {
-      return iterator(DataEnd());
-    }
-  }
-  const_iterator begin() const {
-    if constexpr (checked_iter) {
-      return const_iterator(data(), DataEnd());
-    } else {
-      return const_iterator(data());
-    }
-  }
-  const_iterator end() const {
-    if constexpr (checked_iter) {
-      auto* e = DataEnd();
-      return const_iterator(data(), e, e);
-    } else {
-      return const_iterator(DataEnd());
-    }
-  }
+  iterator begin() { return iterator(data()); }
+  iterator end() { return iterator(DataEnd()); }
+  const_iterator begin() const { return const_iterator(data()); }
+  const_iterator end() const { return const_iterator(DataEnd()); }
 
   reverse_iterator rbegin() { return reverse_iterator(end()); }
   reverse_iterator rend() { return reverse_iterator(begin()); }
@@ -1467,8 +1427,8 @@
   }
   template <typename U>
   void Append(const U*, wtf_size_t);
-  template <typename U, wtf_size_t otherCapacity, typename V, bool f>
-  void AppendVector(const Vector<U, otherCapacity, V, f>&);
+  template <typename U, wtf_size_t otherCapacity, typename V>
+  void AppendVector(const Vector<U, otherCapacity, V>&);
   template <typename Iterator>
   void AppendRange(Iterator begin, Iterator end);
   template <typename U, size_t N>
@@ -1501,12 +1461,9 @@
   void InsertAt(iterator position, U&&);
   template <typename U>
   void InsertAt(iterator position, const U*, wtf_size_t);
-  template <typename U,
-            wtf_size_t otherCapacity,
-            typename OtherAllocator,
-            bool f>
+  template <typename U, wtf_size_t otherCapacity, typename OtherAllocator>
   void InsertVector(wtf_size_t position,
-                    const Vector<U, otherCapacity, OtherAllocator, f>&);
+                    const Vector<U, otherCapacity, OtherAllocator>&);
 
   // Insertion to the front. All of these functions will take O(size())-time.
   // All of the elements in the vector will be moved to the new locations.
@@ -1523,11 +1480,8 @@
   void push_front(U&&);
   template <typename U>
   void push_front(const U*, wtf_size_t);
-  template <typename U,
-            wtf_size_t otherCapacity,
-            typename OtherAllocator,
-            bool f>
-  void PrependVector(const Vector<U, otherCapacity, OtherAllocator, f>&);
+  template <typename U, wtf_size_t otherCapacity, typename OtherAllocator>
+  void PrependVector(const Vector<U, otherCapacity, OtherAllocator>&);
 
   // Remove an element or elements at the specified position. These functions
   // take O(size())-time. All of the elements after the removed ones will be
@@ -1632,7 +1586,7 @@
   const T* const* GetBufferSlot() const { return Base::BufferSlot(); }
 
  private:
-  template <typename, wtf_size_t, typename, bool>
+  template <typename, wtf_size_t, typename>
   friend class Vector;
   // Point the next of the last item. We must not dereference the return value.
   T* DataEnd() { return data() + size(); }
@@ -1698,34 +1652,23 @@
 // Vector out-of-line implementation
 //
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline Vector<T, InlineCapacity, Allocator, checked_iter>::Vector() {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline Vector<T, InlineCapacity, Allocator>::Vector() {
   ANNOTATE_NEW_BUFFER(data(), capacity(), 0);
   size_ = 0;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline Vector<T, InlineCapacity, Allocator, checked_iter>::Vector(
-    wtf_size_t size)
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline Vector<T, InlineCapacity, Allocator>::Vector(wtf_size_t size)
     : Base(size) {
   ANNOTATE_NEW_BUFFER(data(), capacity(), size);
   size_ = size;
   TypeOperations::Initialize(data(), DataEnd());
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline Vector<T, InlineCapacity, Allocator, checked_iter>::Vector(
-    wtf_size_t size,
-    const T& val)
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline Vector<T, InlineCapacity, Allocator>::Vector(wtf_size_t size,
+                                                    const T& val)
     : Base(size) {
   ANNOTATE_NEW_BUFFER(data(), capacity(), size);
   size_ = size;
@@ -1733,11 +1676,8 @@
                                     VectorOperationOrigin::kConstruction);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-Vector<T, InlineCapacity, Allocator, checked_iter>::Vector(const Vector& other)
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+Vector<T, InlineCapacity, Allocator>::Vector(const Vector& other)
     : Base(other.capacity()) {
   ANNOTATE_NEW_BUFFER(data(), capacity(), other.size());
   size_ = other.size();
@@ -1745,13 +1685,9 @@
                                     VectorOperationOrigin::kConstruction);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename Proj, typename>
-Vector<T, InlineCapacity, Allocator, checked_iter>::Vector(const Vector& other,
-                                                           Proj proj)
+Vector<T, InlineCapacity, Allocator>::Vector(const Vector& other, Proj proj)
     : Base(other.capacity()) {
   ANNOTATE_NEW_BUFFER(data(), capacity(), other.size());
   size_ = other.size();
@@ -1760,13 +1696,10 @@
                                     std::move(proj));
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <wtf_size_t otherCapacity>
-Vector<T, InlineCapacity, Allocator, checked_iter>::Vector(
-    const Vector<T, otherCapacity, Allocator, checked_iter>& other)
+Vector<T, InlineCapacity, Allocator>::Vector(
+    const Vector<T, otherCapacity, Allocator>& other)
     : Base(other.capacity()) {
   ANNOTATE_NEW_BUFFER(data(), capacity(), other.size());
   size_ = other.size();
@@ -1774,12 +1707,9 @@
                                     VectorOperationOrigin::kConstruction);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U, wtf_size_t otherCapacity, typename Proj, typename>
-Vector<T, InlineCapacity, Allocator, checked_iter>::Vector(
+Vector<T, InlineCapacity, Allocator>::Vector(
     const Vector<U, otherCapacity, Allocator>& other,
     Proj proj)
     : Base(other.capacity()) {
@@ -1790,13 +1720,10 @@
                                     std::move(proj));
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-Vector<T, InlineCapacity, Allocator, checked_iter>&
-Vector<T, InlineCapacity, Allocator, checked_iter>::operator=(
-    const Vector& other) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+Vector<T, InlineCapacity, Allocator>&
+Vector<T, InlineCapacity, Allocator>::operator=(
+    const Vector<T, InlineCapacity, Allocator>& other) {
   if (&other == this) [[unlikely]] {
     return *this;
   }
@@ -1825,14 +1752,11 @@
   return a == b;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <wtf_size_t otherCapacity>
-Vector<T, InlineCapacity, Allocator, checked_iter>&
-Vector<T, InlineCapacity, Allocator, checked_iter>::operator=(
-    const Vector<T, otherCapacity, Allocator, checked_iter>& other) {
+Vector<T, InlineCapacity, Allocator>&
+Vector<T, InlineCapacity, Allocator>::operator=(
+    const Vector<T, otherCapacity, Allocator>& other) {
   // If the inline capacities match, we should call the more specific
   // template.  If the inline capacities don't match, the two objects
   // shouldn't be allocated the same address.
@@ -1858,17 +1782,12 @@
   return *this;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename Range>
   requires std::ranges::input_range<Range> && std::ranges::sized_range<Range>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::assign(
-    const Range& range) {
+void Vector<T, InlineCapacity, Allocator>::assign(const Range& range) {
   static_assert(
-      !std::is_same_v<Vector<T, InlineCapacity, Allocator, checked_iter>,
-                      Range>,
+      !std::is_same_v<Vector<T, InlineCapacity, Allocator>, Range>,
       "This method is for copying from a collection of a different type.");
 
   {
@@ -1880,23 +1799,19 @@
   base::ranges::copy(range, begin());
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-Vector<T, InlineCapacity, Allocator, checked_iter>::Vector(Vector&& other) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+Vector<T, InlineCapacity, Allocator>::Vector(
+    Vector<T, InlineCapacity, Allocator>&& other) {
   size_ = 0;
   // It's a little weird to implement a move constructor using swap but this
   // way we don't have to add a move constructor to VectorBuffer.
   SwapForMove(std::move(other), VectorOperationOrigin::kConstruction);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-Vector<T, InlineCapacity, Allocator, checked_iter>&
-Vector<T, InlineCapacity, Allocator, checked_iter>::operator=(Vector&& other) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+Vector<T, InlineCapacity, Allocator>&
+Vector<T, InlineCapacity, Allocator>::operator=(
+    Vector<T, InlineCapacity, Allocator>&& other) {
   // Explicitly clearing allows the backing to be freed
   // immediately. In the non-garbage-collected case this is
   // often just slightly moving it earlier as the old backing
@@ -1908,12 +1823,8 @@
   return *this;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-Vector<T, InlineCapacity, Allocator, checked_iter>::Vector(
-    std::initializer_list<T> elements)
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+Vector<T, InlineCapacity, Allocator>::Vector(std::initializer_list<T> elements)
     : Base(base::checked_cast<wtf_size_t>(elements.size())) {
   ANNOTATE_NEW_BUFFER(data(), capacity(), elements.size());
   size_ = static_cast<wtf_size_t>(elements.size());
@@ -1921,12 +1832,9 @@
                                     VectorOperationOrigin::kConstruction);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-Vector<T, InlineCapacity, Allocator, checked_iter>&
-Vector<T, InlineCapacity, Allocator, checked_iter>::operator=(
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+Vector<T, InlineCapacity, Allocator>&
+Vector<T, InlineCapacity, Allocator>::operator=(
     std::initializer_list<T> elements) {
   wtf_size_t input_size = base::checked_cast<wtf_size_t>(elements.size());
   if (size() > input_size) {
@@ -1949,13 +1857,9 @@
   return *this;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-bool Vector<T, InlineCapacity, Allocator, checked_iter>::Contains(
-    const U& value) const {
+bool Vector<T, InlineCapacity, Allocator>::Contains(const U& value) const {
   // Do not reuse Find because the compiler will generate extra code to
   // handle finding the kNotFound-th element in the array.  kNotFound is part
   // of wtf_size_t, but not used as an index due to runtime restrictions.  See
@@ -1970,13 +1874,9 @@
   return false;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-wtf_size_t Vector<T, InlineCapacity, Allocator, checked_iter>::Find(
-    const U& value) const {
+wtf_size_t Vector<T, InlineCapacity, Allocator>::Find(const U& value) const {
   const T* b = data();
   const T* e = DataEnd();
   for (const T* iter = b; iter < e; ++iter) {
@@ -1986,12 +1886,9 @@
   return kNotFound;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-wtf_size_t Vector<T, InlineCapacity, Allocator, checked_iter>::ReverseFind(
+wtf_size_t Vector<T, InlineCapacity, Allocator>::ReverseFind(
     const U& value) const {
   const T* b = data();
   const T* iter = DataEnd();
@@ -2003,13 +1900,9 @@
   return kNotFound;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::Fill(
-    const T& val,
-    wtf_size_t new_size)
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+void Vector<T, InlineCapacity, Allocator>::Fill(const T& val,
+                                                wtf_size_t new_size)
   requires(!Allocator::kIsGarbageCollected)
 {
   if (size() > new_size) {
@@ -2029,11 +1922,8 @@
   size_ = new_size;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::ExpandCapacity(
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+void Vector<T, InlineCapacity, Allocator>::ExpandCapacity(
     wtf_size_t new_min_capacity) {
   wtf_size_t old_capacity = capacity();
   wtf_size_t expanded_capacity = old_capacity;
@@ -2059,11 +1949,8 @@
                    std::max(kInitialVectorSize, expanded_capacity)));
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-T* Vector<T, InlineCapacity, Allocator, checked_iter>::ExpandCapacity(
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+T* Vector<T, InlineCapacity, Allocator>::ExpandCapacity(
     wtf_size_t new_min_capacity,
     T* ptr) {
   if (ptr < data() || ptr >= DataEnd()) {
@@ -2075,24 +1962,17 @@
   return data() + index;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-inline U* Vector<T, InlineCapacity, Allocator, checked_iter>::ExpandCapacity(
+inline U* Vector<T, InlineCapacity, Allocator>::ExpandCapacity(
     wtf_size_t new_min_capacity,
     U* ptr) {
   ExpandCapacity(new_min_capacity);
   return ptr;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::resize(
-    wtf_size_t size) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline void Vector<T, InlineCapacity, Allocator>::resize(wtf_size_t size) {
   if (size <= size_) {
     TypeOperations::Destruct(data() + size, DataEnd());
     ClearUnusedSlots(data() + size, DataEnd());
@@ -2109,12 +1989,8 @@
   size_ = size;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::Shrink(
-    wtf_size_t size) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+void Vector<T, InlineCapacity, Allocator>::Shrink(wtf_size_t size) {
   CHECK_LE(size, size_);
   TypeOperations::Destruct(data() + size, DataEnd());
   ClearUnusedSlots(data() + size, DataEnd());
@@ -2123,11 +1999,8 @@
   size_ = size;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::Grow(wtf_size_t size) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+void Vector<T, InlineCapacity, Allocator>::Grow(wtf_size_t size) {
   DCHECK_GE(size, size_);
   if (size > capacity())
     ExpandCapacity(size);
@@ -2137,12 +2010,8 @@
   size_ = size;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::reserve(
-    wtf_size_t new_capacity) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+void Vector<T, InlineCapacity, Allocator>::reserve(wtf_size_t new_capacity) {
   if (new_capacity <= capacity()) [[unlikely]] {
     return;
   }
@@ -2183,12 +2052,8 @@
   ReallocateBuffer(new_capacity);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline void
-Vector<T, InlineCapacity, Allocator, checked_iter>::ReserveInitialCapacity(
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline void Vector<T, InlineCapacity, Allocator>::ReserveInitialCapacity(
     wtf_size_t initial_capacity) {
   DCHECK(!size_);
   DCHECK(capacity() == INLINE_CAPACITY);
@@ -2203,11 +2068,8 @@
   }
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::ShrinkCapacity(
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+void Vector<T, InlineCapacity, Allocator>::ShrinkCapacity(
     wtf_size_t new_capacity) {
   if (new_capacity >= capacity())
     return;
@@ -2242,13 +2104,9 @@
 
 // Templatizing these is better than just letting the conversion happen
 // implicitly.
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-ALWAYS_INLINE void
-Vector<T, InlineCapacity, Allocator, checked_iter>::push_back(U&& val) {
+ALWAYS_INLINE void Vector<T, InlineCapacity, Allocator>::push_back(U&& val) {
   DCHECK(Allocator::IsAllocationAllowed());
   if (size() != capacity()) [[likely]] {
     MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, data(), capacity(), size_,
@@ -2262,13 +2120,9 @@
   AppendSlowCase(std::forward<U>(val));
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename... Args>
-ALWAYS_INLINE T&
-Vector<T, InlineCapacity, Allocator, checked_iter>::emplace_back(
+ALWAYS_INLINE T& Vector<T, InlineCapacity, Allocator>::emplace_back(
     Args&&... args) {
   DCHECK(Allocator::IsAllocationAllowed());
   if (size() == capacity()) [[unlikely]] {
@@ -2284,14 +2138,10 @@
   return *t;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::Append(
-    const U* data,
-    wtf_size_t data_size) {
+void Vector<T, InlineCapacity, Allocator>::Append(const U* data,
+                                                  wtf_size_t data_size) {
   DCHECK(Allocator::IsAllocationAllowed());
   wtf_size_t new_size = size_ + data_size;
   if (new_size > capacity()) {
@@ -2308,13 +2158,10 @@
   size_ = new_size;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
 NOINLINE PRESERVE_MOST void
-Vector<T, InlineCapacity, Allocator, checked_iter>::AppendSlowCase(U&& val) {
+Vector<T, InlineCapacity, Allocator>::AppendSlowCase(U&& val) {
   DCHECK_EQ(size(), capacity());
 
   typename std::remove_reference<U>::type* ptr = &val;
@@ -2328,47 +2175,33 @@
   ++size_;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-template <typename U, wtf_size_t otherCapacity, typename OtherAllocator, bool f>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::AppendVector(
-    const Vector<U, otherCapacity, OtherAllocator, f>& val) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+template <typename U, wtf_size_t otherCapacity, typename OtherAllocator>
+inline void Vector<T, InlineCapacity, Allocator>::AppendVector(
+    const Vector<U, otherCapacity, OtherAllocator>& val) {
   Append(val.data(), val.size());
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename Iterator>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::AppendRange(
-    Iterator begin,
-    Iterator end) {
+void Vector<T, InlineCapacity, Allocator>::AppendRange(Iterator begin,
+                                                       Iterator end) {
   for (Iterator it = begin; it != end; ++it)
     push_back(*it);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U, size_t N>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::AppendSpan(
-    base::span<U, N> data) {
+void Vector<T, InlineCapacity, Allocator>::AppendSpan(base::span<U, N> data) {
   Append(data.data(), base::checked_cast<wtf_size_t>(data.size()));
 }
 
 // This version of append saves a branch in the case where you know that the
 // vector's capacity is large enough for the append to succeed.
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-ALWAYS_INLINE void
-Vector<T, InlineCapacity, Allocator, checked_iter>::UncheckedAppend(U&& val) {
+ALWAYS_INLINE void Vector<T, InlineCapacity, Allocator>::UncheckedAppend(
+    U&& val) {
 #ifdef ANNOTATE_CONTIGUOUS_CONTAINER
   // Vectors in ASAN builds don't have InlineCapacity.
   push_back(std::forward<U>(val));
@@ -2380,14 +2213,10 @@
 #endif
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::insert(
-    wtf_size_t position,
-    U&& val) {
+inline void Vector<T, InlineCapacity, Allocator>::insert(wtf_size_t position,
+                                                         U&& val) {
   DCHECK(Allocator::IsAllocationAllowed());
   CHECK_LE(position, size());
   typename std::remove_reference<U>::type* data = &val;
@@ -2405,15 +2234,11 @@
   ++size_;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::insert(
-    wtf_size_t position,
-    const U* data,
-    wtf_size_t data_size) {
+void Vector<T, InlineCapacity, Allocator>::insert(wtf_size_t position,
+                                                  const U* data,
+                                                  wtf_size_t data_size) {
   DCHECK(Allocator::IsAllocationAllowed());
   CHECK_LE(position, size());
   wtf_size_t new_size = size_ + data_size;
@@ -2433,77 +2258,51 @@
   size_ = new_size;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::InsertAt(
-    Vector::iterator position,
-    U&& val) {
+void Vector<T, InlineCapacity, Allocator>::InsertAt(Vector::iterator position,
+                                                    U&& val) {
   insert(base::checked_cast<wtf_size_t>(position - begin()), val);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::InsertAt(
-    Vector::iterator position,
-    const U* data,
-    wtf_size_t data_size) {
+void Vector<T, InlineCapacity, Allocator>::InsertAt(Vector::iterator position,
+                                                    const U* data,
+                                                    wtf_size_t data_size) {
   insert(base::checked_cast<wtf_size_t>(position - begin()), data, data_size);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-template <typename U, wtf_size_t otherCapacity, typename OtherAllocator, bool f>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::InsertVector(
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+template <typename U, wtf_size_t otherCapacity, typename OtherAllocator>
+inline void Vector<T, InlineCapacity, Allocator>::InsertVector(
     wtf_size_t position,
-    const Vector<U, otherCapacity, OtherAllocator, f>& val) {
+    const Vector<U, otherCapacity, OtherAllocator>& val) {
   insert(position, val.data(), val.size());
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::push_front(
-    U&& val) {
+inline void Vector<T, InlineCapacity, Allocator>::push_front(U&& val) {
   insert(0, std::forward<U>(val));
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
 template <typename U>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::push_front(
-    const U* data,
-    wtf_size_t data_size) {
+void Vector<T, InlineCapacity, Allocator>::push_front(const U* data,
+                                                      wtf_size_t data_size) {
   insert(0, data, data_size);
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-template <typename U, wtf_size_t otherCapacity, typename OtherAllocator, bool f>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::PrependVector(
-    const Vector<U, otherCapacity, OtherAllocator, f>& val) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+template <typename U, wtf_size_t otherCapacity, typename OtherAllocator>
+inline void Vector<T, InlineCapacity, Allocator>::PrependVector(
+    const Vector<U, otherCapacity, OtherAllocator>& val) {
   insert(0, val.data(), val.size());
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::EraseAt(
-    wtf_size_t position) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline void Vector<T, InlineCapacity, Allocator>::EraseAt(wtf_size_t position) {
   CHECK_LT(position, size());
   T* spot = data() + position;
   spot->~T();
@@ -2515,24 +2314,18 @@
   --size_;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline auto Vector<T, InlineCapacity, Allocator, checked_iter>::erase(
-    iterator position) -> iterator {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline auto Vector<T, InlineCapacity, Allocator>::erase(iterator position)
+    -> iterator {
   wtf_size_t index = static_cast<wtf_size_t>(position - begin());
   EraseAt(index);
   return begin() + index;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline auto Vector<T, InlineCapacity, Allocator, checked_iter>::erase(
-    iterator first,
-    iterator last) -> iterator {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline auto Vector<T, InlineCapacity, Allocator>::erase(iterator first,
+                                                        iterator last)
+    -> iterator {
   DCHECK_LE(first, last);
   const wtf_size_t index = static_cast<wtf_size_t>(first - begin());
   const wtf_size_t diff = static_cast<wtf_size_t>(std::distance(first, last));
@@ -2540,13 +2333,9 @@
   return begin() + index;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::EraseAt(
-    wtf_size_t position,
-    wtf_size_t length) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline void Vector<T, InlineCapacity, Allocator>::EraseAt(wtf_size_t position,
+                                                          wtf_size_t length) {
   SECURITY_DCHECK(position <= size());
   if (!length)
     return;
@@ -2562,31 +2351,24 @@
   size_ -= length;
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline void Vector<T, InlineCapacity, Allocator, checked_iter>::Reverse() {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline void Vector<T, InlineCapacity, Allocator>::Reverse() {
   for (wtf_size_t i = 0; i < size_ / 2; ++i)
     std::swap(at(i), at(size_ - 1 - i));
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-inline void swap(Vector<T, InlineCapacity, Allocator, checked_iter>& a,
-                 Vector<T, InlineCapacity, Allocator, checked_iter>& b) {
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+inline void swap(Vector<T, InlineCapacity, Allocator>& a,
+                 Vector<T, InlineCapacity, Allocator>& b) {
   a.Swap(b);
 }
 
 template <typename T,
           wtf_size_t InlineCapacityA,
           wtf_size_t InlineCapacityB,
-          typename Allocator,
-          bool checked_iter>
-bool operator==(const Vector<T, InlineCapacityA, Allocator, checked_iter>& a,
-                const Vector<T, InlineCapacityB, Allocator, checked_iter>& b) {
+          typename Allocator>
+bool operator==(const Vector<T, InlineCapacityA, Allocator>& a,
+                const Vector<T, InlineCapacityB, Allocator>& b) {
   if (a.size() != b.size())
     return false;
   if (a.empty())
@@ -2598,11 +2380,9 @@
 template <typename T,
           wtf_size_t InlineCapacityA,
           wtf_size_t InlineCapacityB,
-          typename Allocator,
-          bool checked_iter>
-inline bool operator!=(
-    const Vector<T, InlineCapacityA, Allocator, checked_iter>& a,
-    const Vector<T, InlineCapacityB, Allocator, checked_iter>& b) {
+          typename Allocator>
+inline bool operator!=(const Vector<T, InlineCapacityA, Allocator>& a,
+                       const Vector<T, InlineCapacityB, Allocator>& b) {
   return !(a == b);
 }
 
@@ -2634,12 +2414,8 @@
 }  // namespace internal
 
 // Only defined for HeapAllocator. Used when visiting vector object.
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::Trace(
-    auto visitor) const
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+void Vector<T, InlineCapacity, Allocator>::Trace(auto visitor) const
   requires Allocator::kIsGarbageCollected
 {
   static_assert(Allocator::kIsGarbageCollected,
@@ -2678,11 +2454,8 @@
   }
 }
 
-template <typename T,
-          wtf_size_t InlineCapacity,
-          typename Allocator,
-          bool checked_iter>
-void Vector<T, InlineCapacity, Allocator, checked_iter>::ReallocateBuffer(
+template <typename T, wtf_size_t InlineCapacity, typename Allocator>
+void Vector<T, InlineCapacity, Allocator>::ReallocateBuffer(
     wtf_size_t new_capacity) {
   if (new_capacity <= INLINE_CAPACITY) {
     if (HasInlineBuffer()) {
@@ -2723,23 +2496,18 @@
 template <typename T,
           wtf_size_t inline_capacity,
           typename Allocator,
-          bool checked_iter,
           typename U>
-wtf_size_t Erase(Vector<T, inline_capacity, Allocator, checked_iter>& v,
-                 const U& value) {
+wtf_size_t Erase(Vector<T, inline_capacity, Allocator>& v, const U& value) {
   auto it = std::remove(v.begin(), v.end(), value);
   wtf_size_t removed = base::checked_cast<wtf_size_t>(v.end() - it);
   v.erase(it, v.end());
   return removed;
 }
-
 template <typename T,
           wtf_size_t inline_capacity,
           typename Allocator,
-          bool checked_iter,
           typename Pred>
-wtf_size_t EraseIf(Vector<T, inline_capacity, Allocator, checked_iter>& v,
-                   Pred pred) {
+wtf_size_t EraseIf(Vector<T, inline_capacity, Allocator>& v, Pred pred) {
   auto it = std::remove_if(v.begin(), v.end(), pred);
   wtf_size_t removed = base::checked_cast<wtf_size_t>(v.end() - it);
   v.erase(it, v.end());
diff --git a/third_party/blink/renderer/platform/wtf/vector_test.cc b/third_party/blink/renderer/platform/wtf/vector_test.cc
index 01b95abc..aeafea4 100644
--- a/third_party/blink/renderer/platform/wtf/vector_test.cc
+++ b/third_party/blink/renderer/platform/wtf/vector_test.cc
@@ -782,20 +782,6 @@
   }
 }
 
-TEST(VectorTest, IteratorSwitching) {
-  using UncheckedIntVector = Vector<int, 0, PartitionAllocator, false>;
-  using CheckedIntVector = Vector<int, 0, PartitionAllocator, true>;
-  static_assert(std::is_same_v<UncheckedIntVector::iterator,
-                               WTF::UncheckedIterator<int>>);
-  static_assert(std::is_same_v<CheckedIntVector::iterator,
-                               base::CheckedContiguousIterator<int>>);
-
-  // Check if we can compile code using checked iterators.
-  CheckedIntVector v = {3, 2, 1};
-  for ([[maybe_unused]] int i : v) {
-  }
-}
-
 static_assert(VectorTraits<int>::kCanCopyWithMemcpy,
               "int should be copied with memcopy.");
 static_assert(VectorTraits<char>::kCanCopyWithMemcpy,
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index bf54a02c..1ae8f0c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -256,6 +256,21 @@
 # Some additional bugs that are caused by painting problems are also within this section.
 
 
+# --- canvasRenderingContext.placeElement() implementation: g-issues.chromium.org/issues/349835587
+
+wpt_internal/html/canvas/placeElement/placeElement-border-and-padding-box.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement-transform.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement-x-y.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement-explicit-width-height.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement-float-left.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement-position-absolute.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement-width-auto.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement-writingMode.html [ Failure ]
+wpt_internal/html/canvas/placeElement/placeElement-draw-atomically.html [ Failure ]
+
+# --- END canvasRenderingContext.placeElement() implementation
+
 # --- Skia roll test suppressions
 
 crbug.com/1494899 virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-zero-lineto.html [ Failure Pass ]
@@ -1033,51 +1048,6 @@
 crbug.com/1445990 external/wpt/css/css-ui/outline-color-002.html [ Failure ]
 crbug.com/1445990 external/wpt/css/css-ui/outline-color-003.html [ Failure ]
 crbug.com/1445990 external/wpt/css/css-ui/outline-color-004.html [ Failure ]
-# Various issues related to the 'appearance' property:
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-background-attachment-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-background-clip-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-background-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-background-image-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-background-origin-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-background-position-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-background-size-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-block-end-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-block-end-style-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-block-end-width-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-block-start-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-block-start-style-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-block-start-width-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-bottom-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-bottom-left-radius-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-bottom-right-radius-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-bottom-style-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-bottom-width-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-end-end-radius-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-end-start-radius-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-image-outset-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-image-repeat-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-image-slice-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-image-source-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-image-width-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-inline-end-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-inline-end-style-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-inline-end-width-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-inline-start-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-inline-start-style-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-inline-start-width-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-left-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-left-style-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-left-width-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-right-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-right-style-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-right-width-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-start-end-radius-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-start-start-radius-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-top-color-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-top-left-radius-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-top-right-radius-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-top-style-001.html [ Failure ]
-crbug.com/1284251 external/wpt/css/css-ui/compute-kind-widget-generated/kind-of-widget-fallback-meter-border-top-width-001.html [ Failure ]
 
 # [css-flexbox]
 
@@ -2616,11 +2586,11 @@
 crbug.com/364744930 external/wpt/css/css-color/parsing/color-computed-hwb.html [ Crash Pass Timeout ]
 crbug.com/364744930 external/wpt/css/css-color/parsing/color-computed-lab.html [ Crash Pass ]
 crbug.com/364744930 external/wpt/css/css-color/parsing/color-computed-rgb.html [ Crash Pass ]
-crbug.com/364744930 external/wpt/css/css-color/parsing/color-valid-color-function.html [ Crash Failure Pass ]
+crbug.com/364744930 external/wpt/css/css-color/parsing/color-valid-color-function.html [ Crash Pass ]
 crbug.com/364744930 external/wpt/css/css-color/parsing/color-valid-hsl.html [ Crash Pass Timeout ]
 crbug.com/364744930 external/wpt/css/css-color/parsing/color-valid-hwb.html [ Crash Pass Timeout ]
-crbug.com/364744930 external/wpt/css/css-color/parsing/color-valid-lab.html [ Crash Failure Pass ]
-crbug.com/364744930 external/wpt/css/css-color/parsing/color-valid-rgb.html [ Crash Failure Pass Timeout ]
+crbug.com/364744930 external/wpt/css/css-color/parsing/color-valid-lab.html [ Crash Pass ]
+crbug.com/364744930 external/wpt/css/css-color/parsing/color-valid-rgb.html [ Crash Pass Timeout ]
 crbug.com/364700192 external/wpt/css/css-fonts/parsing/font-feature-settings-computed.html [ Crash Pass ]
 crbug.com/364677549 external/wpt/css/css-properties-values-api/register-property-syntax-parsing.html [ Crash Pass Timeout ]
 
@@ -7865,6 +7835,9 @@
 # Gardener 2024-06-28
 crbug.com/349938120 [ Linux ] wpt_internal/webmidi/requestmidiaccess-basic.https.html [ Failure Pass ]
 
+# Remove this when GPUAdapter requestAdapterInfo() method is removed from the web platform.
+crbug.com/335383516 http/tests/inspector-protocol/issues/gpuadapter-requestadapterinfo-deprecation.js [ Skip Timeout ]
+
 # Gardener 2024-07-11
 crbug.com/352455679 [ Linux ] external/wpt/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-focused-nested-containers.html [ Failure Pass Timeout ]
 crbug.com/352455679 [ Linux ] virtual/threaded/external/wpt/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-focused-nested-containers.html [ Failure Pass Timeout ]
@@ -8004,10 +7977,6 @@
 # TODO(crbug.com/362797911): Re-enable this test
 crbug.com/362797911 [ Linux ] fast/text-autosizing/textarea-fontsize-change.html [ Failure Pass ]
 
-# Temporarily ignore failures until https://crrev.com/c/5828823 rolls in.
-crbug.com/364508698 external/wpt/wasm/jsapi/js-string/constants.tentative.any.html [ Failure Pass ]
-crbug.com/364508698 external/wpt/wasm/jsapi/js-string/constants.tentative.any.worker.html [ Failure Pass ]
-
 # These tests only pass because wasm type reflection is implied by the
 # experimental JSPI flag. Temporarily disable them as we remove the obsolete
 # flag implication on the V8 side, and re-enable them with up-to-date
@@ -8036,3 +8005,32 @@
 
 # Gardener 2024-09-10
 crbug.com/364306254 [ Linux ] fast/events/middleClickAutoscroll-drag.html [ Failure Pass ]
+
+# Temporarily add TestExpectation for win11-23h2-rel try builder testing
+# To be removed once crbug.com/359039651 is resolved
+crbug.com/365127791 [ Win11 ] css2.1/t1202-counter-04-b.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] css2.1/t1202-counters-04-b.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] custom-elements/form-validation-bubble-appearance.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] fast/canvas/fillText-emoji.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] fast/forms/file/file-appearance-bidi-filenames.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] fast/loader/json-document-appearance.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] fast/ruby/base-shorter-than-text.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] fast/ruby/nested-ruby.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.js [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] media/track/track-cue-rendering-ruby.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] svg/text/bidi-textlength.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] svg/text/surrogate-pair-queries.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/text-antialias/color-emoji.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/text-antialias/emoji-vs-system-fallback.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/text-antialias/emoticons.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/text-antialias/fallback-traits-fixup.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/text-antialias/unicode-fallback-font.html [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-reload-navigation-reload.html [ Pass Crash ]
+crbug.com/365127791 [ Win11 ] external/wpt/media-capabilities/encodingInfo.any.worker.html [ Pass Crash ]
+crbug.com/365127791 [ Win11 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-reload-navigation-reload.html [ Pass Crash ]
+crbug.com/365127791 [ Win11 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/conv2d.https.any.html?gpu [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/conv2d.https.any.worker.html?gpu [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/sign.https.any.html?gpu [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/sign.https.any.worker.html?gpu [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/pooling.https.any.html?gpu [ Pass Failure ]
+crbug.com/365127791 [ Win11 ] virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/pooling.https.any.worker.html?gpu [ Pass Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 099fe8b..b42af965e 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -421,6 +421,7 @@
   },
   {
     "prefix": "shared_array_buffer_on_desktop",
+    "owners": ["clamy@chromium.org", "chrome-secure-web-and-net@chromium.org"],
     "platforms": ["Linux"],
     "bases": ["external/wpt/wasm/jsapi/memory",
               "fast/workers/worker-atomics-wait.html",
@@ -1849,6 +1850,7 @@
   },
   {
     "prefix": "coop-restrict-properties",
+    "owners": ["clamy@chromium.org", "arichiv@chromium.org"],
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [ "external/wpt/html/cross-origin-opener-policy/tentative/restrict-properties" ],
     "args": ["--enable-features=CoopRestrictProperties"],
@@ -2184,6 +2186,7 @@
   },
   {
     "prefix": "produce-compile-hints",
+    "owners": ["marja@chromium.org"],
     "platforms": ["Linux"],
     "bases": [
       "external/wpt/fetch",
@@ -3377,22 +3380,6 @@
     "expires": "Jun 1, 2025",
     "owners": ["fergal@chromium.org", "rakina@chromium.org"]
   },
-  {
-    "prefix": "import-assertions-enabled",
-    "platforms": ["Linux", "Mac", "Win"],
-    "bases": [
-      "external/wpt/html/semantics/scripting-1/the-script-element/css-module",
-      "external/wpt/html/semantics/scripting-1/the-script-element/import-attributes",
-      "external/wpt/html/semantics/scripting-1/the-script-element/json-module",
-      "external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/css-import-in-worker.any.js",
-      "external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/microtasks/with-import-assertions.any.js",
-      "http/tests/inspector-protocol/css/constructed-stylesheet-source-url.js"
-    ],
-    "exclusive_tests": "ALL",
-    "args": ["--js-flags=--harmony-import-assertions"],
-    "expires": "Aug 1, 2024",
-    "owners": ["syg@chromium.org"]
-  },
   "select-parser-relaxation is needed in addition to",
   "stylable-select-disabled because we might launch parser changes first.",
   {
@@ -3541,6 +3528,25 @@
     ]
   },
   {
+    "prefix": "expect-no-embedded-resources",
+    "platforms": [
+      "Linux",
+      "Mac",
+      "Win"
+    ],
+    "bases": [
+      "http/tests/expect-no-embedded-resources"
+    ],
+    "exclusive_tests": "ALL",
+    "args": [
+      "--enable-features=DocumentPolicyExpectNoEmbeddedResources"
+    ],
+    "expires": "Jan 14, 2025",
+    "owners": [
+      "alexnj@chromium.org"
+    ]
+  },
+  {
     "prefix": "document-picture-in-picture-user-activation",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [
diff --git a/third_party/blink/web_tests/dom/mutation-event-tests/editing/selection/crash-on-drag-with-mutation-events-expected.txt b/third_party/blink/web_tests/dom/mutation-event-tests/editing/selection/crash-on-drag-with-mutation-events-expected.txt
index 08e9fba..61dde9b 100644
--- a/third_party/blink/web_tests/dom/mutation-event-tests/editing/selection/crash-on-drag-with-mutation-events-expected.txt
+++ b/third_party/blink/web_tests/dom/mutation-event-tests/editing/selection/crash-on-drag-with-mutation-events-expected.txt
@@ -1,2 +1,5 @@
 CONSOLE WARNING: Listener added for a 'DOMNodeInserted' mutation event. This event type is deprecated, and will be removed from this browser VERY soon. Usage of this event listener will cause performance issues today, and represents a large risk of imminent site breakage. Consider using MutationObserver instead. See https://chromestatus.com/feature/5083947249172480 for more information.
+CONSOLE WARNING: We don't execute document.execCommand() this time, because it is called recursively.
+CONSOLE WARNING: We don't execute document.execCommand() this time, because it is called recursively.
+CONSOLE WARNING: We don't execute document.execCommand() this time, because it is called recursively.
 PASSED, no crash
diff --git a/third_party/blink/web_tests/editing/deleting/5847330-2-expected.txt b/third_party/blink/web_tests/editing/deleting/5847330-2-expected.txt
index 122621f1..584988e 100644
--- a/third_party/blink/web_tests/editing/deleting/5847330-2-expected.txt
+++ b/third_party/blink/web_tests/editing/deleting/5847330-2-expected.txt
@@ -1 +1,3 @@
 This tests for a crash when deleting inside an empty list item that is an editable root.
+
+
diff --git a/third_party/blink/web_tests/editing/deleting/delete-across-editable-content-boundaries-1-expected.txt b/third_party/blink/web_tests/editing/deleting/delete-across-editable-content-boundaries-1-expected.txt
index 328951a..42b5c6f 100644
--- a/third_party/blink/web_tests/editing/deleting/delete-across-editable-content-boundaries-1-expected.txt
+++ b/third_party/blink/web_tests/editing/deleting/delete-across-editable-content-boundaries-1-expected.txt
@@ -1,2 +1,2 @@
 This tests the deletion of non-editable content that both starts and ends in editable roots - it should successfully be removed. There should be no visible content in the markup below. <radr://problem/5026848>
-| 
+| <br>
diff --git a/third_party/blink/web_tests/editing/deleting/delete-and-cleanup-expected.txt b/third_party/blink/web_tests/editing/deleting/delete-and-cleanup-expected.txt
index 910cd4bb..2e90417 100644
--- a/third_party/blink/web_tests/editing/deleting/delete-and-cleanup-expected.txt
+++ b/third_party/blink/web_tests/editing/deleting/delete-and-cleanup-expected.txt
@@ -1,6 +1,6 @@
 This test checks that deletion does not leave unnecessary nested divs.
 
-PASS confirmedMarkup is ''
+PASS confirmedMarkup is '<br>'
 PASS confirmedMarkup is '<br>'
 PASS confirmedMarkup is '<div id="mydiv"><br></div>'
 PASS confirmedMarkup is '<br>'
diff --git a/third_party/blink/web_tests/editing/deleting/delete-and-cleanup.html b/third_party/blink/web_tests/editing/deleting/delete-and-cleanup.html
index b9c55091..0eb5957c 100644
--- a/third_party/blink/web_tests/editing/deleting/delete-and-cleanup.html
+++ b/third_party/blink/web_tests/editing/deleting/delete-and-cleanup.html
@@ -33,7 +33,7 @@
     shouldBe("confirmedMarkup", "'" + expected + "'");
 }
 
-testDelete("div", "Hello", "");
+testDelete("div", "Hello", "<br>");
 testDelete("div", "<div>Hello</div>", "<br>");
 testDelete("div", "<div id=\"mydiv\">Hello</div>", "<div id=\"mydiv\"><br></div>");
 testDelete("div", "<div><div>Hello</div></div>", "<br>");
diff --git a/third_party/blink/web_tests/editing/deleting/delete_select_all.html b/third_party/blink/web_tests/editing/deleting/delete_select_all.html
index 53e0a8d..17dfce0 100644
--- a/third_party/blink/web_tests/editing/deleting/delete_select_all.html
+++ b/third_party/blink/web_tests/editing/deleting/delete_select_all.html
@@ -30,7 +30,7 @@
     'delete',
     [
         '<div contenteditable>',
-            '|',
+            '|<br>',
         '</div>',
     ].join('')),
     '2-1 Delete visibility:hidden');
@@ -44,7 +44,7 @@
     'delete',
     [
         '<div contenteditable>',
-            '|',
+            '|<br>',
         '</div>',
     ].join('')),
     '2-2 Delete display:none');
diff --git a/third_party/blink/web_tests/editing/execCommand/delete-non-editable-range-crash.html b/third_party/blink/web_tests/editing/execCommand/delete-non-editable-range-crash.html
index 97ccc77..3008a7b 100644
--- a/third_party/blink/web_tests/editing/execCommand/delete-non-editable-range-crash.html
+++ b/third_party/blink/web_tests/editing/execCommand/delete-non-editable-range-crash.html
@@ -26,13 +26,13 @@
       '<div contenteditable="true">',
         '<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">',
           '<span>^foo<br></span>',
-          'barbarbar|',
+          'bar<br>barbar',
         '</blockquote>',
-        '<span contenteditable="false">',
-          '<span contenteditable="true"></span>',
-          '<ol>bar</ol>',
-        '</span>',
+        '<span contenteditable="false">|',
+          '<span contenteditable="true">',
+          '<ol></ol>',
+        '</span><ol>bar</ol></span>',
       '</div>'
     ],
-  'The test passes if it does not rash.');
+  'The test passes if it does not crash.');
 </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 2a7e0d9..bd598eb2 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
@@ -165918,7 +165918,7 @@
        ]
       ],
       "webkit-line-clamp-047.html": [
-       "2e546c8dc9294b2cca54d4e061c9ede160eb474a",
+       "cb66eb714c510579f16b101010188ca6b22a32ba",
        [
         null,
         [
@@ -165943,6 +165943,19 @@
         {}
        ]
       ],
+      "webkit-line-clamp-049.html": [
+       "807f83e511061ad98c535ee6dde4470503e9e294",
+       [
+        null,
+        [
+         [
+          "/css/css-overflow/line-clamp/reference/webkit-line-clamp-049-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "webkit-line-clamp-block-in-inline-001.html": [
        "75d1de3bf5bcf5d00d6980de4a70845e9f7ae8e4",
        [
@@ -188913,6 +188926,58 @@
        {}
       ]
      ],
+     "table-as-item-cell-percentage-001.html": [
+      "eb9a620b0b47a12c1c959b838a84ee83efd29282",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "table-as-item-cell-percentage-002.html": [
+      "7b7556c1f07402a18e3e2ba0d8ca9b46e566a0f1",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "table-as-item-cell-percentage-003.html": [
+      "c8bfa9a2fd075231520e7712f0212cfd1852e8f2",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "table-as-item-cell-percentage-004.html": [
+      "a716ed25d97fa228fa9711677dbff19d7004d2ec",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "table-cell-baseline-static-position.html": [
       "e3f5236645f03be99aa0abd3d330623413445263",
       [
@@ -320732,6 +320797,10 @@
       []
      ],
      "parsing": {
+      "WEB_FEATURES.yml": [
+       "616accbe0a70c17b6f53137667f52bbbd1891eb3",
+       []
+      ],
       "display-computed-expected.txt": [
        "2f57da9606a75c5e3704443035db8419dfdad85e",
        []
@@ -337304,7 +337373,11 @@
         []
        ],
        "webkit-line-clamp-047-ref.html": [
-        "83c5ab851c1c48dda7fb608de0bbed502b439b58",
+        "3f1e31ab2224a778f8061084df0a1854c71bdaeb",
+        []
+       ],
+       "webkit-line-clamp-049-ref.html": [
+        "7a3591b5eead803163450acc1cfed4a3e8fd39d1",
         []
        ],
        "webkit-line-clamp-block-in-inline-001-ref.html": [
@@ -367471,7 +367544,7 @@
        []
       ],
       "ba-fledge-util.sub.js": [
-       "1c9f051d9d3aa9b91183ab27cc5a486356587810",
+       "c534a8a6c624dfc88f050d968d72fa0c14b14aea",
        []
       ],
       "ba-public-keys": [
@@ -367564,7 +367637,15 @@
       ]
      },
      "server-response.https.window_1-4-expected.txt": [
-      "b9bbe1aa69c8b78589e0909e21c8f7a42c3e13ed",
+      "b2d8ccff9132560d14a7da31a17c05722328b82a",
+      []
+     ],
+     "server-response.https.window_5-8-expected.txt": [
+      "9c5a0ceb6e8d1e85fc17fe82698b8c8658d31083",
+      []
+     ],
+     "server-response.https.window_9-12-expected.txt": [
+      "32703368c4337abebd67e1d6f27384e947532828",
       []
      ],
      "third_party": {
@@ -410892,7 +410973,7 @@
     },
     "resources": {
      "utils.js": [
-      "e4a1e898598e6190da25fbdfe53b483af844d08c",
+      "c6b6010b3b0c2ea60bb2394b42273427109d9a20",
       []
      ],
      "utils_validation.js": [
@@ -521988,7 +522069,7 @@
      ]
     ],
     "notify-event-transient-user-activation.https.html": [
-     "04be865fe5bce4d9cf640f240da8c852f817f39b",
+     "5e37b1180ad1da5937328e903c51da59d4e4c9d6",
      [
       null,
       {
@@ -545190,7 +545271,7 @@
       ]
      ],
      "server-response.https.window.js": [
-      "918dffefedd58dad55be0c009275464b3f97ea19",
+      "1556b5c1f8a7f814643b9b7dabe2d4914fa557ea",
       [
        "fledge/tentative/server-response.https.window.html?1-4",
        {
@@ -545226,6 +545307,110 @@
          [
           "variant",
           "?1-4"
+         ],
+         [
+          "variant",
+          "?5-8"
+         ],
+         [
+          "variant",
+          "?9-12"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "fledge/tentative/server-response.https.window.html?5-8",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/resources/testdriver.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/ba-fledge-util.sub.js"
+         ],
+         [
+          "script",
+          "resources/fledge-util.sub.js"
+         ],
+         [
+          "script",
+          "third_party/cbor-js/cbor.js"
+         ],
+         [
+          "script",
+          "/common/subset-tests.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ],
+         [
+          "variant",
+          "?1-4"
+         ],
+         [
+          "variant",
+          "?5-8"
+         ],
+         [
+          "variant",
+          "?9-12"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "fledge/tentative/server-response.https.window.html?9-12",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/resources/testdriver.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/ba-fledge-util.sub.js"
+         ],
+         [
+          "script",
+          "resources/fledge-util.sub.js"
+         ],
+         [
+          "script",
+          "third_party/cbor-js/cbor.js"
+         ],
+         [
+          "script",
+          "/common/subset-tests.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ],
+         [
+          "variant",
+          "?1-4"
+         ],
+         [
+          "variant",
+          "?5-8"
+         ],
+         [
+          "variant",
+          "?9-12"
          ]
         ],
         "timeout": "long"
@@ -697505,6 +697690,225 @@
        }
       ]
      ],
+     "dequantizeLinear.https.any.js": [
+      "2939121c24182e49cccfafa2df26ca2e56fa1356",
+      [
+       "webnn/conformance_tests/dequantizeLinear.https.any.html?cpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API dequantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/dequantizeLinear.https.any.html?gpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API dequantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/dequantizeLinear.https.any.html?npu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API dequantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/dequantizeLinear.https.any.worker.html?cpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API dequantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/dequantizeLinear.https.any.worker.html?gpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API dequantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/dequantizeLinear.https.any.worker.html?npu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API dequantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ]
+     ],
      "div.https.any.js": [
       "69183ee1097b1aacd36f863383fc5b846741e0a4",
       [
@@ -706046,6 +706450,225 @@
        }
       ]
      ],
+     "quantizeLinear.https.any.js": [
+      "acae378d80b42eb9f8e7b648d98dfa78bafe58d4",
+      [
+       "webnn/conformance_tests/quantizeLinear.https.any.html?cpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API quantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/quantizeLinear.https.any.html?gpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API quantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/quantizeLinear.https.any.html?npu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API quantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/quantizeLinear.https.any.worker.html?cpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API quantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/quantizeLinear.https.any.worker.html?gpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API quantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "webnn/conformance_tests/quantizeLinear.https.any.worker.html?npu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "test WebNN API quantizeLinear operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils.js"
+         ],
+         [
+          "timeout",
+          "long"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ]
+     ],
      "reciprocal.https.any.js": [
       "a97c00e2b996ba13b052a4ec79e2208ab5b2e384",
       [
@@ -721680,6 +722303,195 @@
        }
       ]
      ],
+     "scatterND.https.any.js": [
+      "18fcb40e892eb4e9237f5cb1061fa6ad82e16669",
+      [
+       "webnn/validation_tests/scatterND.https.any.html?cpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "validation tests for WebNN API scatterND operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils_validation.js"
+         ]
+        ]
+       }
+      ],
+      [
+       "webnn/validation_tests/scatterND.https.any.html?gpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "validation tests for WebNN API scatterND operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils_validation.js"
+         ]
+        ]
+       }
+      ],
+      [
+       "webnn/validation_tests/scatterND.https.any.html?npu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "validation tests for WebNN API scatterND operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils_validation.js"
+         ]
+        ]
+       }
+      ],
+      [
+       "webnn/validation_tests/scatterND.https.any.worker.html?cpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "validation tests for WebNN API scatterND operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils_validation.js"
+         ]
+        ]
+       }
+      ],
+      [
+       "webnn/validation_tests/scatterND.https.any.worker.html?gpu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "validation tests for WebNN API scatterND operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils_validation.js"
+         ]
+        ]
+       }
+      ],
+      [
+       "webnn/validation_tests/scatterND.https.any.worker.html?npu",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "validation tests for WebNN API scatterND operation"
+         ],
+         [
+          "global",
+          "window,dedicatedworker"
+         ],
+         [
+          "variant",
+          "?cpu"
+         ],
+         [
+          "variant",
+          "?gpu"
+         ],
+         [
+          "variant",
+          "?npu"
+         ],
+         [
+          "script",
+          "../resources/utils_validation.js"
+         ]
+        ]
+       }
+      ]
+     ],
      "sigmoid.https.any.js": [
       "271bef211169047249dac7ee51250fa0a72d23a1",
       [
@@ -764080,7 +764892,7 @@
          ]
         ],
         "pointer_mouse.py": [
-         "511bda663d99f2f83f74505c39a51024efd92afc",
+         "7077d7bba4678b8825cbe83556f18b4b6db1e0ea",
          [
           null,
           {}
@@ -764117,21 +764929,21 @@
          ]
         ],
         "pointer_pen.py": [
-         "44ac435a21ec2c26d9a02bb22c08c2702820ec8f",
+         "7b68d3de057db102ccaec01282a202721b16af24",
          [
           null,
           {}
          ]
         ],
         "pointer_touch.py": [
-         "6edc7f832026703512869f9015294e0a2425504d",
+         "f036de7c744c1d5bb3376640bdffa9ef5ca79daa",
          [
           null,
           {}
          ]
         ],
         "wheel.py": [
-         "0a1af029b1e92fa1ba94bdb60679edc8b93dd0ba",
+         "3129e9b0e63ca15092f6264ce8eecfef471d0abd",
          [
           null,
           {}
@@ -766473,7 +767285,7 @@
         ]
        ],
        "pointer_mouse.py": [
-        "53ec6d2c13530654ec752f9da913e348a2996d92",
+        "f8683ce45156c7d79c49753eca04c55ffc2b4f16",
         [
          null,
          {}
@@ -766494,14 +767306,14 @@
         ]
        ],
        "pointer_pen.py": [
-        "7c384ca97d8f66bc22b4f77f7fbfc7b1db10cbe3",
+        "bf71a20c4d2ce89ffc40c4338deaefb5a4f41ddd",
         [
          null,
          {}
         ]
        ],
        "pointer_touch.py": [
-        "70ffd68e9ea8f3206886ddeb1dcd06d22ba359d8",
+        "b85b2e6ef38195d26e0e580edf34a141efc19eba",
         [
          null,
          {}
@@ -766531,7 +767343,7 @@
         ]
        ],
        "wheel.py": [
-        "0840327ff902ad146ca26637e9c9ac8d481da3a1",
+        "1c9bf082270429406080baab3373d5ff3f506aed",
         [
          null,
          {}
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_continue_index.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_continue_index.any.js
index 2ac55bce..8bf329c4 100644
--- a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_continue_index.any.js
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_continue_index.any.js
@@ -1,9 +1,6 @@
 // META: global=window,worker
-// META: title=IDBCursor.continue()
+// META: title=IDBCursor.continue() - index
 // META: script=resources/support.js
-// @author Microsoft <https://www.microsoft.com>
-// @author Odin Hørthe Omdal <mailto:odinho@opera.com>
-// @author Intel <http://www.intel.com>
 
 'use strict';
 
@@ -56,7 +53,7 @@
       count++;
     });
   };
-}, "IDBCursor.continue() - index - iterate to the next record");
+}, "Iterate to the next record");
 
 async_test(t => {
   let dbObj = {};
@@ -85,7 +82,7 @@
       t.done();
     });
   };
-}, "IDBCursor.continue() - index - attempt to pass a key parameter that is not a valid key");
+}, "Attempt to pass a key parameter that is not a valid key");
 
 async_test(t => {
   let dbObj = {};
@@ -120,7 +117,7 @@
       count++;
     });
   };
-}, "IDBCursor.continue() - index - attempt to iterate to the previous record when the direction is set for the next record");
+}, "Attempt to iterate to the previous record when the direction is set for the next record");
 
 async_test(t => {
   let dbObj = {};
@@ -166,7 +163,7 @@
       count++;
     });
   };
-}, "IDBCursor.continue() - index - attempt to iterate to the next record when the direction is set for the previous record");
+}, "Attempt to iterate to the next record when the direction is set for the previous record");
 
 async_test(t => {
   let dbObj = {};
@@ -212,7 +209,7 @@
       cursor.continue(expected[count] ? expected[count].iKey : undefined);
     });
   };
-}, "IDBCursor.continue() - index - iterate using 'prevunique'");
+}, "Iterate using 'prevunique'");
 
 async_test(t => {
   let dbObj = {};
@@ -258,7 +255,7 @@
       cursor.continue(expected[count] ? expected[count].iKey : undefined);
     });
   };
-}, "IDBCursor.continue() - index - iterate using nextunique");
+}, "Iterate using nextunique");
 
 async_test(t => {
   let db;
@@ -309,3 +306,99 @@
     });
   }
 }, "If the cursor's source or effective object store has been deleted, the implementation MUST throw a DOMException of type InvalidStateError");
+
+async_test(t => {
+  let db;
+  let count = 0;
+  const records = [
+    { pKey: "primaryKey_0", obj: { iKey: "iKey_0" } },
+    { pKey: "primaryKey_1", obj: { iKey: "iKey_1" } },
+    { pKey: "primaryKey_2", obj: { iKey: "iKey_2" } }
+  ];
+
+  const expected = [
+    ["primaryKey_2", "iKey_2"],
+    ["primaryKey_0", "iKey_0"]
+  ];
+
+  let open_rq = createdb(t);
+  open_rq.onupgradeneeded = function (e) {
+    db = e.target.result;
+    var objStore = db.createObjectStore("test", { keyPath: ["pKey", "obj.iKey"] });
+    objStore.createIndex("index", ["pKey", "obj.iKey"]);
+
+    for (var i = 0; i < records.length; i++)
+      objStore.add(records[i]);
+  };
+
+  open_rq.onsuccess = function (e) {
+    var cursor_rq = db.transaction("test", "readwrite", { durability: 'relaxed' })
+      .objectStore("test")
+      .index("index")
+      .openCursor(null, "prev");
+
+    cursor_rq.onsuccess = t.step_func(function (e) {
+      var cursor = e.target.result;
+      if (!cursor) {
+        assert_equals(count, 2, "cursor run count");
+        t.done();
+      }
+
+      if (count === 0) {
+        e.target.source.objectStore.delete(["primaryKey_1", "iKey_1"]);
+      }
+      assert_array_equals(cursor.key, expected[count], "primary key");
+
+      cursor.continue();
+      count++;
+    });
+  }
+}, "Delete next element, and iterate to it");
+
+async_test(t => {
+  let db;
+  let count = 0;
+  const records = [
+    { pKey: "primaryKey_0", obj: { iKey: "iKey_0" } },
+    { pKey: "primaryKey_2", obj: { iKey: "iKey_2" } }
+  ];
+
+  const expected = [
+    ["primaryKey_2", "iKey_2"],
+    ["primaryKey_1", "iKey_1"],
+    ["primaryKey_0", "iKey_0"]
+  ];
+
+  let open_rq = createdb(t);
+  open_rq.onupgradeneeded = function (e) {
+    db = e.target.result;
+    var objStore = db.createObjectStore("test", { keyPath: "pKey" });
+    objStore.createIndex("index", ["pKey", "obj.iKey"]);
+
+    for (var i = 0; i < records.length; i++)
+      objStore.add(records[i]);
+  };
+
+  open_rq.onsuccess = function (e) {
+    var cursor_rq = db.transaction("test", "readwrite", { durability: 'relaxed' })
+      .objectStore("test")
+      .index("index")
+      .openCursor(null, "prev");
+
+    cursor_rq.onsuccess = t.step_func(function (e) {
+      var cursor = e.target.result;
+      if (!cursor) {
+        assert_equals(count, 3, "cursor run count");
+        t.done();
+      }
+
+      if (count === 0) {
+        e.target.source.objectStore.add({ pKey: "primaryKey_1", obj: { iKey: "iKey_1" } });
+      }
+      assert_array_equals(cursor.key, expected[count], "primary key");
+
+      cursor.continue();
+      count++;
+    });
+  }
+}, "Add next element, and iterate to it");
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_iterating_index.htm b/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_iterating_index.htm
deleted file mode 100644
index be480a0..0000000
--- a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_iterating_index.htm
+++ /dev/null
@@ -1,53 +0,0 @@
-<!DOCTYPE html>
-<title>IDBCursor.continue() - index - delete next element, and iterate to it</title>
-<link rel="author" href="mailto:odinho@opera.com" title="Odin Hørthe Omdal">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/support.js"></script>
-
-<script>
-    var db,
-      count = 0,
-      t = async_test(),
-      records = [ { pKey: "primaryKey_0", obj: { iKey: "iKey_0" }},
-                  { pKey: "primaryKey_1", obj: { iKey: "iKey_1" }},
-                  { pKey: "primaryKey_2", obj: { iKey: "iKey_2" }} ],
-
-      expected = [ [ "primaryKey_2", "iKey_2" ],
-                   [ "primaryKey_0", "iKey_0" ] ];
-
-    var open_rq = createdb(t);
-    open_rq.onupgradeneeded = function(e) {
-        db = e.target.result;
-        var objStore = db.createObjectStore("test", {keyPath:["pKey", "obj.iKey"]});
-        objStore.createIndex("index", [ "pKey", "obj.iKey" ]);
-
-        for (var i = 0; i < records.length; i++)
-            objStore.add(records[i]);
-    };
-
-    open_rq.onsuccess = function(e) {
-        var cursor_rq = db.transaction("test", "readwrite", {durability: 'relaxed'})
-                          .objectStore("test")
-                          .index("index")
-                          .openCursor(null, "prev");
-
-        cursor_rq.onsuccess = t.step_func(function(e) {
-            var cursor = e.target.result;
-            if (!cursor) {
-                assert_equals(count, 2, "cursor run count");
-                t.done();
-            }
-
-            if (count === 0) {
-                e.target.source.objectStore.delete(["primaryKey_1", "iKey_1"]);
-            }
-            assert_array_equals(cursor.key, expected[count], "primary key");
-
-            cursor.continue();
-            count++;
-        });
-    };
-</script>
-
-<div id="log"> </div>
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_iterating_index2.htm b/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_iterating_index2.htm
deleted file mode 100644
index d7af6af..0000000
--- a/third_party/blink/web_tests/external/wpt/IndexedDB/idbcursor_iterating_index2.htm
+++ /dev/null
@@ -1,53 +0,0 @@
-<!DOCTYPE html>
-<title>IDBCursor.continue() - index - add next element, and iterate to it</title>
-<link rel="author" href="mailto:odinho@opera.com" title="Odin Hørthe Omdal">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/support.js"></script>
-
-<script>
-    var db,
-      count = 0,
-      t = async_test(),
-      records = [ { pKey: "primaryKey_0", obj: { iKey: "iKey_0" }},
-                  { pKey: "primaryKey_2", obj: { iKey: "iKey_2" }} ],
-
-      expected = [ [ "primaryKey_2", "iKey_2" ],
-                   [ "primaryKey_1", "iKey_1" ],
-                   [ "primaryKey_0", "iKey_0" ] ];
-
-    var open_rq = createdb(t);
-    open_rq.onupgradeneeded = function(e) {
-        db = e.target.result;
-        var objStore = db.createObjectStore("test", {keyPath:"pKey"});
-        objStore.createIndex("index", [ "pKey", "obj.iKey" ]);
-
-        for (var i = 0; i < records.length; i++)
-            objStore.add(records[i]);
-    };
-
-    open_rq.onsuccess = function(e) {
-        var cursor_rq = db.transaction("test", "readwrite", {durability: 'relaxed'})
-                          .objectStore("test")
-                          .index("index")
-                          .openCursor(null, "prev");
-
-        cursor_rq.onsuccess = t.step_func(function(e) {
-            var cursor = e.target.result;
-            if (!cursor) {
-                assert_equals(count, 3, "cursor run count");
-                t.done();
-            }
-
-            if (count === 0) {
-                e.target.source.objectStore.add({ pKey: "primaryKey_1", obj: { iKey: "iKey_1" } });
-            }
-            assert_array_equals(cursor.key, expected[count], "primary key");
-
-            cursor.continue();
-            count++;
-        });
-    };
-</script>
-
-<div id="log"> </div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-relative-color-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-relative-color-expected.txt
index c3e72a0..e09eaeb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-relative-color-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-relative-color-expected.txt
@@ -1,2096 +1,38 @@
 This is a testharness.js-based test.
-Found 1046 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from hsl(120deg 20% 50% / .5) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: rgb(from rgba(102, 153, 102, 0.5) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "rgb(from rgb(from rebeccapurple r g b) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rgb(from rebeccapurple r g b) r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rebeccapurple 0 0 0).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple   )" but got "color(srgb   )"
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: rgb(from rebeccapurple 0 0 0 / 0).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple    / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.2 0.6)\nExpected: rgb(from rebeccapurple 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0 0.6)\nExpected: rgb(from rebeccapurple r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0)\nExpected: rgb(from rebeccapurple r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 25 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.2 0.6)\nExpected: rgb(from rebeccapurple 25 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 25 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0980392 0.6)\nExpected: rgb(from rebeccapurple r 25 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0980392)\nExpected: rgb(from rebeccapurple r g 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / .25)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.25)\nExpected: rgb(from rebeccapurple r g b / 0.25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 25 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 25 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 25 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0980392 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 25 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.0980392 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g b / .20)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 0.20).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.6)\nExpected: rgb(from rebeccapurple 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.2)\nExpected: rgb(from rebeccapurple r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.2)\nExpected: rgb(from rebeccapurple r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 25 g b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.2 0.6 / 0.25)\nExpected: rgb(from rebeccapurple 25 g b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 25 b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0980392 0.6 / 0.25)\nExpected: rgb(from rebeccapurple r 25 b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g 25 / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0980392 / 0.25)\nExpected: rgb(from rebeccapurple r g 25 / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 25 g b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.4 0.6 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 25 g b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 25 b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0980392 0.6 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 25 b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g 25 / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.0980392 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 25 / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.6 0.4)\nExpected: rgb(from rebeccapurple g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.00392157 0.4)\nExpected: rgb(from rebeccapurple b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4)\nExpected: rgb(from rebeccapurple r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.00392157 0.00392157 0.00392157)\nExpected: rgb(from rebeccapurple alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.00313725 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.00313725 0.00313725 0.00313725 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.00313725
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 20% 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r 20% 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 10 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0392157 0.2)\nExpected: rgb(from rebeccapurple r 10 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 0% 10 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.0392157 0.0392157)\nExpected: rgb(from rebeccapurple 0% 10 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 10 +/- 0.01, expected 10 but got 0.0392157
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 20% 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.0392157 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 20% 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 10 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0392157 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 10 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 0% 10 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.0392157 0.0392157 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 0% 10 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple calc(r) calc(g) calc(b))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple calc(r) calc(g) calc(b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
+Found 17 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] e.style['color'] = "rgb(from rebeccapurple r calc(g * 2) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.0392157)\nExpected: rgb(from rebeccapurple r calc(2 * g) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(g * 2) 10)\nExpected: rgb(from rebeccapurple r calc(2 * g) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc( * g) )" but got "rgb(from rebeccapurple r calc(g * ) )"
 [FAIL] e.style['color'] = "rgb(from rebeccapurple b calc(r * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.0392157)\nExpected: rgb(from rebeccapurple b calc(0.5 * r) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple b calc(r * 0.5) 10)\nExpected: rgb(from rebeccapurple b calc(0.5 * r) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple b calc( * r) )" but got "rgb(from rebeccapurple b calc(r * ) )"
 [FAIL] e.style['color'] = "rgb(from rebeccapurple r calc(g * .5 + g * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r calc((0.5 * g) + (0.5 * g)) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.5 +/- 0.01, expected 0.5 but got 0.4
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(g * 0.5 + g * 0.5) 10)\nExpected: rgb(from rebeccapurple r calc((0.5 * g) + (0.5 * g)) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc(( * g) + ( * g)) )" but got "rgb(from rebeccapurple r calc(g *  + g * ) )"
 [FAIL] e.style['color'] = "rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r calc((0.5 * b) - (0.5 * g)) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.5 +/- 0.01, expected 0.5 but got 0.4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from rebeccapurple none none none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rebeccapurple none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / none)\nExpected: rgb(from rebeccapurple none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / none)\nExpected: rgb(from rebeccapurple r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20% 40% 60% / 80%) r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from rgb(20% 40% 60% / 80%) r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / none)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(none none none) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rgb(0, 0, 0) r g b).\nError: assert_equals: Color format is correct. expected "rgb(from rgb(, , ) r g b)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "rgb(from rgb(none none none / none) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: rgb(from rgba(0, 0, 0, 0) r g b / alpha).\nError: assert_equals: Color format is correct. expected "rgb(from rgba(, , , ) r g b / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "rgb(from rgb(20% none 60%) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0 0.6)\nExpected: rgb(from rgb(51, 0, 153) r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from rgb(20% 40% 60% / none) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: rgb(from rgba(51, 102, 153, 0) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from color-mix(in srgb, red, red) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: rgb(from color-mix(in srgb, red, red) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from hsl(120deg 20% 50% / .5) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: rgb(from rgba(102, 153, 102, 0.5) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "rgba(from rgb(from rebeccapurple r g b) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rgb(from rebeccapurple r g b) r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rebeccapurple 0 0 0).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple   )" but got "color(srgb   )"
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: rgb(from rebeccapurple 0 0 0 / 0).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple    / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.2 0.6)\nExpected: rgb(from rebeccapurple 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0 0.6)\nExpected: rgb(from rebeccapurple r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0)\nExpected: rgb(from rebeccapurple r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 25 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.2 0.6)\nExpected: rgb(from rebeccapurple 25 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 25 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0980392 0.6)\nExpected: rgb(from rebeccapurple r 25 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0980392)\nExpected: rgb(from rebeccapurple r g 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / .25)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.25)\nExpected: rgb(from rebeccapurple r g b / 0.25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 25 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 25 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 25 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0980392 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 25 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.0980392 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g b / .20)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 0.20).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.6)\nExpected: rgb(from rebeccapurple 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.2)\nExpected: rgb(from rebeccapurple r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.2)\nExpected: rgb(from rebeccapurple r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 25 g b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.2 0.6 / 0.25)\nExpected: rgb(from rebeccapurple 25 g b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 25 b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0980392 0.6 / 0.25)\nExpected: rgb(from rebeccapurple r 25 b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g 25 / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0980392 / 0.25)\nExpected: rgb(from rebeccapurple r g 25 / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 25 g b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.4 0.6 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 25 g b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 25 b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0980392 0.6 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 25 b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g 25 / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.0980392 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 25 / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.6 0.4)\nExpected: rgb(from rebeccapurple g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.00392157 0.4)\nExpected: rgb(from rebeccapurple b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4)\nExpected: rgb(from rebeccapurple r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.00392157 0.00392157 0.00392157)\nExpected: rgb(from rebeccapurple alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.00313725 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.00313725 0.00313725 0.00313725 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.00313725
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 20% 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r 20% 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 10 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0392157 0.2)\nExpected: rgb(from rebeccapurple r 10 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 0% 10 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.0392157 0.0392157)\nExpected: rgb(from rebeccapurple 0% 10 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 10 +/- 0.01, expected 10 but got 0.0392157
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 20% 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.0392157 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 20% 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 10 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0392157 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 10 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 0% 10 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.0392157 0.0392157 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 0% 10 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple calc(r) calc(g) calc(b))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple calc(r) calc(g) calc(b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(b * 0.5 - g * 0.5) 10)\nExpected: rgb(from rebeccapurple r calc((0.5 * b) - (0.5 * g)) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc(( * b) - ( * g)) )" but got "rgb(from rebeccapurple r calc(b *  - g * ) )"
 [FAIL] e.style['color'] = "rgba(from rebeccapurple r calc(g * 2) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.0392157)\nExpected: rgb(from rebeccapurple r calc(2 * g) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(g * 2) 10)\nExpected: rgb(from rebeccapurple r calc(2 * g) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc( * g) )" but got "rgb(from rebeccapurple r calc(g * ) )"
 [FAIL] e.style['color'] = "rgba(from rebeccapurple b calc(r * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.0392157)\nExpected: rgb(from rebeccapurple b calc(0.5 * r) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple b calc(r * 0.5) 10)\nExpected: rgb(from rebeccapurple b calc(0.5 * r) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple b calc( * r) )" but got "rgb(from rebeccapurple b calc(r * ) )"
 [FAIL] e.style['color'] = "rgba(from rebeccapurple r calc(g * .5 + g * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r calc((0.5 * g) + (0.5 * g)) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.5 +/- 0.01, expected 0.5 but got 0.4
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(g * 0.5 + g * 0.5) 10)\nExpected: rgb(from rebeccapurple r calc((0.5 * g) + (0.5 * g)) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc(( * g) + ( * g)) )" but got "rgb(from rebeccapurple r calc(g *  + g * ) )"
 [FAIL] e.style['color'] = "rgba(from rebeccapurple r calc(b * .5 - g * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r calc((0.5 * b) - (0.5 * g)) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.5 +/- 0.01, expected 0.5 but got 0.4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from rebeccapurple none none none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rebeccapurple none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / none)\nExpected: rgb(from rebeccapurple none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / none)\nExpected: rgb(from rebeccapurple r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20% 40% 60% / 80%) r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from rgb(20% 40% 60% / 80%) r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / none)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(none none none) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rgb(0, 0, 0) r g b).\nError: assert_equals: Color format is correct. expected "rgb(from rgb(, , ) r g b)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "rgba(from rgb(none none none / none) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: rgb(from rgba(0, 0, 0, 0) r g b / alpha).\nError: assert_equals: Color format is correct. expected "rgb(from rgba(, , , ) r g b / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "rgba(from rgb(20% none 60%) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0 0.6)\nExpected: rgb(from rgb(51, 0, 153) r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from rgb(20% 40% 60% / none) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: rgb(from rgba(51, 102, 153, 0) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from color-mix(in srgb, red, red) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: rgb(from color-mix(in srgb, red, red) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / .5) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hsl(from hsl(from rebeccapurple h s l) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from hsl(from rebeccapurple h s l) h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0 0% 0%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple 0 0% 0%).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple  % %)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0deg 0% 0%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple 0deg 0% 0%).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple deg % %)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0 0% 0% / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rebeccapurple 0 0% 0% / 0).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple  % % / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0deg 0% 0% / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rebeccapurple 0deg 0% 0% / 0).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple deg % % / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple 0 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple 0deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h 0% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4)\nExpected: hsl(from rebeccapurple h 0% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0)\nExpected: hsl(from rebeccapurple h s l / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) 0 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 0 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) 0deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 0deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h 0% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h 0% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s l / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 25 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2)\nExpected: hsl(from rebeccapurple 25 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 25deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2)\nExpected: hsl(from rebeccapurple 25deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h 20% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.32 0.48)\nExpected: hsl(from rebeccapurple h 20% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.1 0.3)\nExpected: hsl(from rebeccapurple h s 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l / .25)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.25)\nExpected: hsl(from rebeccapurple h s l / .25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) 25 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 25 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) 25deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 25deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h 20% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.32 0.4 0.48 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h 20% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.1 0.2 0.3 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s l / .2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / .2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h l s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.3 0.7)\nExpected: hsl(from rebeccapurple h l s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h alpha l / s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.396 0.404)\nExpected: hsl(from rebeccapurple h alpha l / s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h l l / l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.24 0.56)\nExpected: hsl(from rebeccapurple h l l / l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.01 0.0099 0.0101)\nExpected: hsl(from rebeccapurple h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h l s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3 0.5 0.7 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h l s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h alpha l / s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3968 0.4 0.4032)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h alpha l / s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h l l / l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.24 0.4 0.56)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h l l / l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.007936 0.008 0.008064 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.007936
-[FAIL] e.style['color'] = "hsl(from rebeccapurple calc(h) calc(s) calc(l))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple calc(h) calc(s) calc(l)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) calc(h) calc(s) calc(l) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hsl(from rebeccapurple none none none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / none)\nExpected: hsl(from rebeccapurple none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / none)\nExpected: hsl(from rebeccapurple h s l / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple none s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple none s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / .5) h s none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / .5) h s l / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / none)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s l / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / .5) none s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.4 0.4 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) none s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.6
-[FAIL] e.style['color'] = "hsl(from hsl(none none none) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rgb(0, 0, 0) h s l).\nError: assert_equals: Color format is correct. expected "hsl(from rgb(, , ) h s l)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsl(from hsl(none none none / none) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rgba(0, 0, 0, 0) h s l / alpha).\nError: assert_equals: Color format is correct. expected "hsl(from rgba(, , , ) h s l / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsl(from hsl(120deg none 50% / .5) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.5 0.5 / 0.5)\nExpected: hsl(from rgba(128, 128, 128, 0.5) h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 128 +/- 0.01, expected 128 but got 0.5
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / none) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0)\nExpected: hsl(from rgba(102, 153, 102, 0) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hsl(from hsl(none 20% 50% / .5) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.4 0.4 / 0.5)\nExpected: hsl(from rgba(153, 102, 102, 0.5) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 153 +/- 0.01, expected 153 but got 0.6
-[FAIL] e.style['color'] = "hsl(from color-mix(in srgb, red, red) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: hsl(from color-mix(in srgb, red, red) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / .5) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hsla(from hsl(from rebeccapurple h s l) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from hsl(from rebeccapurple h s l) h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0 0% 0%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple 0 0% 0%).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple  % %)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0deg 0% 0%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple 0deg 0% 0%).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple deg % %)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0 0% 0% / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rebeccapurple 0 0% 0% / 0).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple  % % / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0deg 0% 0% / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rebeccapurple 0deg 0% 0% / 0).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple deg % % / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple 0 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple 0deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h 0% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4)\nExpected: hsl(from rebeccapurple h 0% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0)\nExpected: hsl(from rebeccapurple h s l / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) 0 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 0 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) 0deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 0deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h 0% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h 0% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s l / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 25 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2)\nExpected: hsl(from rebeccapurple 25 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 25deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2)\nExpected: hsl(from rebeccapurple 25deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h 20% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.32 0.48)\nExpected: hsl(from rebeccapurple h 20% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.1 0.3)\nExpected: hsl(from rebeccapurple h s 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l / .25)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.25)\nExpected: hsl(from rebeccapurple h s l / .25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) 25 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 25 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) 25deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 25deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h 20% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.32 0.4 0.48 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h 20% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.1 0.2 0.3 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s l / .2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / .2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h l s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.3 0.7)\nExpected: hsl(from rebeccapurple h l s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h alpha l / s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.396 0.404)\nExpected: hsl(from rebeccapurple h alpha l / s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h l l / l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.24 0.56)\nExpected: hsl(from rebeccapurple h l l / l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.01 0.0099 0.0101)\nExpected: hsl(from rebeccapurple h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h l s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3 0.5 0.7 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h l s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h alpha l / s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3968 0.4 0.4032)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h alpha l / s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h l l / l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.24 0.4 0.56)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h l l / l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.007936 0.008 0.008064 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.007936
-[FAIL] e.style['color'] = "hsla(from rebeccapurple calc(h) calc(s) calc(l))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple calc(h) calc(s) calc(l)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) calc(h) calc(s) calc(l) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hsla(from rebeccapurple none none none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / none)\nExpected: hsl(from rebeccapurple none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / none)\nExpected: hsl(from rebeccapurple h s l / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple none s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple none s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / .5) h s none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / .5) h s l / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / none)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s l / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / .5) none s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.4 0.4 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) none s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.6
-[FAIL] e.style['color'] = "hsla(from hsl(none none none) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rgb(0, 0, 0) h s l).\nError: assert_equals: Color format is correct. expected "hsl(from rgb(, , ) h s l)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsla(from hsl(none none none / none) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rgba(0, 0, 0, 0) h s l / alpha).\nError: assert_equals: Color format is correct. expected "hsl(from rgba(, , , ) h s l / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsla(from hsl(120deg none 50% / .5) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.5 0.5 / 0.5)\nExpected: hsl(from rgba(128, 128, 128, 0.5) h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 128 +/- 0.01, expected 128 but got 0.5
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / none) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0)\nExpected: hsl(from rgba(102, 153, 102, 0) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hsla(from hsl(none 20% 50% / .5) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.4 0.4 / 0.5)\nExpected: hsl(from rgba(153, 102, 102, 0.5) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 153 +/- 0.01, expected 153 but got 0.6
-[FAIL] e.style['color'] = "hsla(from color-mix(in srgb, red, red) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: hsl(from color-mix(in srgb, red, red) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w b)" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w b / alpha)" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hwb(from hsl(120deg 20% 50% / .5) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: hwb(from rgba(102, 153, 102, 0.5) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hwb(from hwb(from rebeccapurple h w b) h w b)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from hwb(from rebeccapurple h w b) h w b)" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0 0% 0%)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0 0% 0%)" but got "color(srgb 1 0 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0deg 0% 0%)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0deg 0% 0%)" but got "color(srgb 1 0 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0 0% 0% / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0 0% 0% / 0)" but got "color(srgb 1 0 0 / 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0deg 0% 0% / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0deg 0% 0% / 0)" but got "color(srgb 1 0 0 / 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0 w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0 w b / alpha)" but got "color(srgb 0.6 0.2 0.2)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0deg w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0deg w b / alpha)" but got "color(srgb 0.6 0.2 0.2)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h 0% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h 0% b / alpha)" but got "color(srgb 0.3 0 0.6)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w 0% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w 0% / alpha)" but got "color(srgb 0.6 0.2 1)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w b / 0)" but got "color(srgb 0.4 0.2 0.6 / 0)"
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) 0 w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) 0 w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) 0deg w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) 0deg w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h 0% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.3 0.6 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h 0% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.6 1 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 25 w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 25 w b / alpha)" but got "color(srgb 0.6 0.366667 0.2)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 25deg w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 25deg w b / alpha)" but got "color(srgb 0.6 0.366667 0.2)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h 20% b / alpha)" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w 20% / alpha)" but got "color(srgb 0.5 0.2 0.8)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b / .2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.2)\nExpected: hwb(from rebeccapurple h w b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) 25 w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) 25 w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) 25deg w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) 25deg w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.8 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w b / .2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w b / .2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h b w)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h b w)" but got "color(srgb 0.6 0.4 0.8)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h alpha w / b)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h alpha w / b)" but got "color(srgb 0.405 0.01 0.8)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w w / w)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w w / w)" but got "color(srgb 0.5 0.2 0.8)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h alpha alpha / alpha)" but got "color(srgb 0.5 0.01 0.99)"
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h b w)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.8 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h b w).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h alpha w / b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.008 0.404 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h alpha w / b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w w / w)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w w / w).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.008 0.5 0.992 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.008
-[FAIL] e.style['color'] = "hwb(from rebeccapurple calc(h) calc(w) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple calc(h) calc(w) calc(b))" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) calc(h) calc(w) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) calc(h) calc(w) calc(b) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hwb(from rebeccapurple none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple none none none)" but got "color(srgb 1 0 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple none none none / none)" but got "color(srgb 1 0 0 / none)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w none)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w none)" but got "color(srgb 0.6 0.2 1)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w none / alpha)" but got "color(srgb 0.6 0.2 1)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w b / none)" but got "color(srgb 0.4 0.2 0.6 / none)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple none w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple none w b / alpha)" but got "color(srgb 0.6 0.2 0.2)"
-[FAIL] e.style['color'] = "hwb(from hwb(120deg 20% 50% / .5) h w none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 1 0.2 / 0.5)\nExpected: hwb(from rgba(51, 128, 51, 0.5) h w none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hwb(from hwb(120deg 20% 50% / .5) h w b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.2 / none)\nExpected: hwb(from rgba(51, 128, 51, 0.5) h w b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hwb(from hwb(120deg 20% 50% / .5) none w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.2 0.2 / 0.5)\nExpected: hwb(from rgba(51, 128, 51, 0.5) none w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.5
-[FAIL] e.style['color'] = "hwb(from hwb(none none none) h w b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: hwb(from rgb(255, 0, 0) h w b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 255 +/- 0.01, expected 255 but got 1
-[FAIL] e.style['color'] = "hwb(from hwb(none none none / none) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0 / 0)\nExpected: hwb(from rgba(255, 0, 0, 0) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 255 +/- 0.01, expected 255 but got 1
-[FAIL] e.style['color'] = "hwb(from hwb(120deg none 50% / .5) h w b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.5 0 / 0.5)\nExpected: hwb(from rgba(0, 128, 0, 0.5) h w b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 128 +/- 0.01, expected 128 but got 0.5
-[FAIL] e.style['color'] = "hwb(from hwb(120deg 20% 50% / none) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.2 / 0)\nExpected: hwb(from rgba(51, 128, 51, 0) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hwb(from hwb(none 20% 50% / .5) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.2 0.2 / 0.5)\nExpected: hwb(from rgba(128, 51, 51, 0.5) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 128 +/- 0.01, expected 128 but got 0.5
-[FAIL] e.style['color'] = "hwb(from color-mix(in srgb, red, red) h w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from color-mix(in srgb, red, red) h w b / alpha)" but got "color(srgb 1 0 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a b)" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a b / alpha)" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l a b / alpha).\nError: assert_equals: Color format is correct. expected "lab(from lab(   / ) l a b / alpha)" but got "lab(   / )"
-[FAIL] e.style['color'] = "lab(from lab(200 300 400 / 500%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(100 300 400)\nExpected: lab(from lab(100 300 400) l a b / alpha).\nError: assert_equals: Color format is correct. expected "lab(from lab(  ) l a b / alpha)" but got "lab(  )"
-[FAIL] e.style['color'] = "lab(from lab(-200 -300 -400 / -500%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(0 -300 -400 / 0)\nExpected: lab(from lab(0 -300 -400 / 0) l a b / alpha).\nError: assert_equals: Color format is correct. expected "lab(from lab( - - / ) l a b / alpha)" but got "lab( - - / )"
-[FAIL] e.style['color'] = "lab(from lab(from lab(25 20 50) l a b) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(from lab(25 20 50) l a b) l a b)" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "lab(from color(display-p3 0 0 0) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from color(display-p3 0 0 0) l a b / alpha)" but got "lab(0 0 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) 0 0 0)" but got "lab(0 0 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) 0 0 0 / 0)" but got "lab(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) 0 a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) 0 a b / alpha)" but got "lab(0 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l 0 b / alpha)" but got "lab(25 0 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a 0 / alpha)" but got "lab(25 20 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a b / 0)" but got "lab(25 20 50 / 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) 0 a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(0 20 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) 0 a b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 0 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 0 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l a 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a b / 0)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0)\nExpected: lab(from lab(25 20 50 / 0.4) l a b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) 35 a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) 35 a b / alpha)" but got "lab(35 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l 35 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l 35 b / alpha)" but got "lab(25 35 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a 35 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a 35 / alpha)" but got "lab(25 20 35)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b / .35)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0.35)\nExpected: lab(from lab(25 20 50) l a b / 0.35).\nError: assert_equals: Color format is correct. expected "lab(from lab(  ) l a b / )" but got "lab(   / )"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) 35 a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(35 20 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) 35 a b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l 35 b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 35 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l 35 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a 35 / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 35 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l a 35 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a b / .35)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0.35)\nExpected: lab(from lab(25 20 50 / 0.4) l a b / .35).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(0.7 45 30 / 40%) 200 300 400 / 500)" should set the property value
-  Colors do not match.\nActual:   lab(100 300 400)\nExpected: lab(from lab(0.7 45 30 / 0.4) 200 300 400 / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "lab(from lab(0.7 45 30 / 40%) -200 -300 -400 / -500)" should set the property value
-  Colors do not match.\nActual:   lab(0 -300 -400 / 0)\nExpected: lab(from lab(0.7 45 30 / 0.4) -200 -300 -400 / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l b a)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l b a)" but got "lab(25 50 20)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a a / a)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a a / a)" but got "lab(25 20 20)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l b a)" should set the property value
-  Colors do not match.\nActual:   lab(25 50 20 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l b a).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 20 +/- 0.01, expected 20 but got 50
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a a / a)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 20)\nExpected: lab(from lab(25 20 50 / 0.4) l a a / a).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) calc(l) calc(a) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) calc(l) calc(a) calc(b))" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) calc(l) calc(a) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "lab(from lab(   / ) calc(l) calc(a) calc(b) / calc(alpha))" but got "lab(   / )"
-[FAIL] e.style['color'] = "lab(from lab(50 -30 40) calc(l - 20) a b)" should set the property value
-  Colors do not match.\nActual:   lab(30 -30 40)\nExpected: lab(from lab(50 -30 40) calc(-20 + l) a b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(b * 0.5 - g * 0.5) 10)\nExpected: rgb(from rebeccapurple r calc((0.5 * b) - (0.5 * g)) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc(( * b) - ( * g)) )" but got "rgb(from rebeccapurple r calc(b *  - g * ) )"
 [FAIL] e.style['color'] = "lab(from lab(50 -30 40) l calc(a / 3) calc(b / 2))" should set the property value
-  Colors do not match.\nActual:   lab(50 -10 20)\nExpected: lab(from lab(50 -30 40) l calc(0.333333 * a) calc(0.5 * b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 3
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) none none none)" but got "lab(none none none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) none none none / none)" but got "lab(none none none / none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a none)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a none)" but got "lab(25 20 none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a none / alpha)" but got "lab(25 20 none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a b / none)" but got "lab(25 20 50 / none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a none / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 none / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l a none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a b / none)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / none)\nExpected: lab(from lab(25 20 50 / 0.4) l a b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lab(from lab(none none none) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(none none none) l a b)" but got "lab(0 0 0)"
-[FAIL] e.style['color'] = "lab(from lab(none none none / none) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(none none none / none) l a b / alpha)" but got "lab(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 none 50) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 none 50) l a b)" but got "lab(25 0 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / none) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50 / none) l a b / alpha)" but got "lab(25 20 50 / 0)"
-[FAIL] e.style['color'] = "lab(from color-mix(in lab, lab(25 20 50), lab(25 20 50)) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from color-mix(in lab, lab(25 20 50), lab(25 20 50)) l a b / alpha)" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b)" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b / alpha)" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a b / alpha).\nError: assert_equals: Color format is correct. expected "oklab(from oklab(   / ) l a b / alpha)" but got "oklab(   / )"
-[FAIL] e.style['color'] = "oklab(from oklab(2 3 4 / 500%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(1 3 4)\nExpected: oklab(from oklab(1 3 4) l a b / alpha).\nError: assert_equals: Color format is correct. expected "oklab(from oklab(  ) l a b / alpha)" but got "oklab(  )"
-[FAIL] e.style['color'] = "oklab(from oklab(-2 -3 -4 / -500%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0 -3 -4 / 0)\nExpected: oklab(from oklab(0 -3 -4 / 0) l a b / alpha).\nError: assert_equals: Color format is correct. expected "oklab(from oklab( - - / ) l a b / alpha)" but got "oklab( - - / )"
-[FAIL] e.style['color'] = "oklab(from oklab(from oklab(0.25 0.2 0.5) l a b) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(from oklab(0.25 0.2 0.5) l a b) l a b)" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from color(display-p3 0 0 0) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from color(display-p3 0 0 0) l a b / alpha)" but got "oklab(0 0 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) 0 0 0)" but got "oklab(0 0 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) 0 0 0 / 0)" but got "oklab(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) 0 a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) 0 a b / alpha)" but got "oklab(0 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l 0 b / alpha)" but got "oklab(0.25 0 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a 0 / alpha)" but got "oklab(0.25 0.2 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b / 0)" but got "oklab(0.25 0.2 0.5 / 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) 0 a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0 0.2 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) 0 a b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a b / 0)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / 0)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) 0.35 a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) 0.35 a b / alpha)" but got "oklab(0.35 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l 0.35 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l 0.35 b / alpha)" but got "oklab(0.25 0.35 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a 0.35 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a 0.35 / alpha)" but got "oklab(0.25 0.2 0.35)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b / 0.35)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b / 0.35)" but got "oklab(0.25 0.2 0.5 / 0.35)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) 0.35 a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.35 0.2 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) 0.35 a b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l 0.35 b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.35 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l 0.35 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a 0.35 / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.35 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a 0.35 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a b / .35)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / 0.35)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a b / .35).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.7 0.45 0.3 / 40%) 2 3 4 / 500)" should set the property value
-  Colors do not match.\nActual:   oklab(1 3 4)\nExpected: oklab(from oklab(0.7 0.45 0.3 / 0.4) 2 3 4 / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "oklab(from oklab(0.7 0.45 0.3 / 40%) -2 -3 -4 / -500)" should set the property value
-  Colors do not match.\nActual:   oklab(0 -3 -4 / 0)\nExpected: oklab(from oklab(0.7 0.45 0.3 / 0.4) -2 -3 -4 / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l b a)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l b a)" but got "oklab(0.25 0.5 0.2)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a a / a)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a a / a)" but got "oklab(0.25 0.2 0.2 / 0.2)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l b a)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.5 0.2 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l b a).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.2 +/- 0.01, expected 0.2 but got 0.5
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a a / a)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.2 / 0.2)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a a / a).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 2, expected 0.5 +/- 0.01, expected 0.5 but got 0.2
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) calc(l) calc(a) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) calc(l) calc(a) calc(b))" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) calc(l) calc(a) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) calc(l) calc(a) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "oklab(from oklab(   / ) calc(l) calc(a) calc(b) / calc(alpha))" but got "oklab(   / )"
-[FAIL] e.style['color'] = "oklab(from oklab(0.7 0.25 -0.15) calc(l - 0.2) a b)" should set the property value
-  Colors do not match.\nActual:   oklab(0.5 0.25 -0.15)\nExpected: oklab(from oklab(0.7 0.25 -0.15) calc(-0.2 + l) a b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
+  Colors do not match.\nActual:   lab(from lab(50 -30 40) l calc(a / 3) calc(b / 2))\nExpected: lab(from lab(50 -30 40) l calc(0.333333 * a) calc(0.5 * b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 0.333333 +/- 0.01, expected 0.333333 but got 3
 [FAIL] e.style['color'] = "oklab(from oklab(0.7 0.25 -0.15) l calc(a / 2) calc(b / 3))" should set the property value
-  Colors do not match.\nActual:   oklab(0.7 0.125 -0.05)\nExpected: oklab(from oklab(0.7 0.25 -0.15) l calc(0.5 * a) calc(0.333333 * b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 3
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) none none none)" but got "oklab(none none none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) none none none / none)" but got "oklab(none none none / none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a none)" but got "oklab(0.25 0.2 none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a none / alpha)" but got "oklab(0.25 0.2 none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b / none)" but got "oklab(0.25 0.2 0.5 / none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a none / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 none / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a b / none)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / none)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklab(from oklab(none none none) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(none none none) l a b)" but got "oklab(0 0 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(none none none / none) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(none none none / none) l a b / alpha)" but got "oklab(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 none 0.5) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 none 0.5) l a b)" but got "oklab(0.25 0 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / none) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5 / none) l a b / alpha)" but got "oklab(0.25 0.2 0.5 / 0)"
-[FAIL] e.style['color'] = "oklab(from color-mix(in oklab, oklab(0.25 0.2 0.5), oklab(0.25 0.2 0.5)) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from color-mix(in oklab, oklab(0.25 0.2 0.5), oklab(0.25 0.2 0.5)) l a b / alpha)" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h)" but got "lch(0.7 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h / alpha)" but got "lch(0.7 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c h / alpha).\nError: assert_equals: Color format is correct. expected "lch(from lch(   / ) l c h / alpha)" but got "lch(   / )"
-[FAIL] e.style['color'] = "lch(from lch(200 300 400 / 500%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(100 300 40)\nExpected: lch(from lch(100 300 40) l c h / alpha).\nError: assert_equals: Color format is correct. expected "lch(from lch(  ) l c h / alpha)" but got "lch(  )"
-[FAIL] e.style['color'] = "lch(from lch(-200 -300 -400 / -500%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0 0 320 / 0)\nExpected: lch(from lch(0 0 320 / 0) l c h / alpha).\nError: assert_equals: Color format is correct. expected "lch(from lch(   / ) l c h / alpha)" but got "lch(   / )"
-[FAIL] e.style['color'] = "lch(from lch(from lch(0.7 45 30) l c h) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(from lch(0.7 45 30) l c h) l c h)" but got "lch(0.7 45 30)"
-[FAIL] e.style['color'] = "lch(from color(display-p3 0 0 0) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from color(display-p3 0 0 0) l c h / alpha)" but got "lch(0 0 0)"
-[FAIL] e.style['color'] = "lch(from lab(0.7 45 30) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lab(0.7 45 30) l c h / alpha)" but got "lch(0.7 54.0833 33.6901)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 0 0)" but got "lch(0 0 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 0 0deg)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 0 0deg)" but got "lch(0 0 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 0 0 / 0)" but got "lch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 0 0deg / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 0 0deg / 0)" but got "lch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 c h / alpha)" but got "lch(0 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l 0 h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l 0 h / alpha)" but got "lch(0.7 0 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c 0 / alpha)" but got "lch(0.7 45 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c 0deg / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c 0deg / alpha)" but got "lch(0.7 45 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h / 0)" but got "lch(0.7 45 30 / 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 0 c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0 45 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) 0 c h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l 0 h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 0 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l 0 h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 0 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c 0deg / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 0 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c 0deg / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c h / 0)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / 0)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c h / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 25 c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 25 c h / alpha)" but got "lch(25 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l 25 h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l 25 h / alpha)" but got "lch(0.7 25 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c 25 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c 25 / alpha)" but got "lch(0.7 45 25)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c 25deg / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c 25deg / alpha)" but got "lch(0.7 45 25)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h / 0.25)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h / 0.25)" but got "lch(0.7 45 30 / 0.25)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 25 c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(25 45 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) 25 c h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l 25 h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 25 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l 25 h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 25 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c 25deg / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 25 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c 25deg / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c h / .25)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / 0.25)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c h / .25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 200 300 400 / 500)" should set the property value
-  Colors do not match.\nActual:   lch(100 300 40)\nExpected: lch(from lch(0.7 45 30 / 0.4) 200 300 400 / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) -200 -300 -400 / -500)" should set the property value
-  Colors do not match.\nActual:   lch(0 0 320 / 0)\nExpected: lch(from lch(0.7 45 30 / 0.4) -200 -300 -400 / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 50 120 400deg / 500)" should set the property value
-  Colors do not match.\nActual:   lch(50 120 40)\nExpected: lch(from lch(0.7 45 30 / 0.4) 50 120 400deg / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 50 120 -400deg / -500)" should set the property value
-  Colors do not match.\nActual:   lch(50 120 320 / 0)\nExpected: lch(from lch(0.7 45 30 / 0.4) 50 120 -400deg / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c c / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c c / alpha)" but got "lch(0.7 45 45)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c c / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 45 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c c / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 2, expected 30 +/- 0.01, expected 30 but got 45
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) calc(l) calc(c) calc(h))" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) calc(l) calc(c) calc(h))" but got "lch(0.7 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) calc(l) calc(c) calc(h) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "lch(from lch(   / ) calc(l) calc(c) calc(h) / calc(alpha))" but got "lch(   / )"
-[FAIL] e.style['color'] = "lch(from lch(50 100 300) calc(l - 20) c h)" should set the property value
-  Colors do not match.\nActual:   lch(30 100 300)\nExpected: lch(from lch(50 100 300) calc(-20 + l) c h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
+  Colors do not match.\nActual:   oklab(from oklab(0.7 0.25 -0.15) l calc(a / 2) calc(b / 3))\nExpected: oklab(from oklab(0.7 0.25 -0.15) l calc(0.5 * a) calc(0.333333 * b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 0.5 +/- 0.01, expected 0.5 but got 2
 [FAIL] e.style['color'] = "lch(from lch(50 100 300) l calc(c / 2) h)" should set the property value
-  Colors do not match.\nActual:   lch(50 50 300)\nExpected: lch(from lch(50 100 300) l calc(0.5 * c) h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
+  Colors do not match.\nActual:   lch(from lch(50 100 300) l calc(c / 2) h)\nExpected: lch(from lch(50 100 300) l calc(0.5 * c) h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 0.5 +/- 0.01, expected 0.5 but got 2
 [FAIL] e.style['color'] = "lch(from lch(50 100 300) l c calc(h * 2.5))" should set the property value
-  Colors do not match.\nActual:   lch(50 100 30)\nExpected: lch(from lch(50 100 300) l c calc(2.5 * h)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) none none none)" but got "lch(none none none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) none none none / none)" but got "lch(none none none / none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c none)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c none)" but got "lch(0.7 45 none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c none / alpha)" but got "lch(0.7 45 none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h / none)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h / none)" but got "lch(0.7 45 30 / none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c none / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 none / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c h / none)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / none)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c h / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lch(from lch(none none none) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(none none none) l c h)" but got "lch(0 0 0)"
-[FAIL] e.style['color'] = "lch(from lch(none none none / none) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(none none none / none) l c h / alpha)" but got "lch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 none 30) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 none 30) l c h)" but got "lch(0.7 0 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / none) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30 / none) l c h / alpha)" but got "lch(0.7 45 30 / 0)"
-[FAIL] e.style['color'] = "lch(from color-mix(in lch, lch(70 45 30), lch(70 45 30)) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from color-mix(in lch, lch(70 45 30), lch(70 45 30)) l c h / alpha)" but got "lch(70 45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h)" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h / alpha)" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c h / alpha).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(   / ) l c h / alpha)" but got "oklch(   / )"
-[FAIL] e.style['color'] = "oklch(from oklch(2 3 400 / 500%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(1 3 40)\nExpected: oklch(from oklch(1 3 40) l c h / alpha).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(  ) l c h / alpha)" but got "oklch(  )"
-[FAIL] e.style['color'] = "oklch(from oklch(-2 -3 -400 / -500%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0 0 320 / 0)\nExpected: oklch(from oklch(0 0 320 / 0) l c h / alpha).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(   / ) l c h / alpha)" but got "oklch(   / )"
-[FAIL] e.style['color'] = "oklch(from oklch(from oklch(0.7 0.45 30) l c h) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(from oklch(0.7 0.45 30) l c h) l c h)" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from color(display-p3 0 0 0) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from color(display-p3 0 0 0) l c h / alpha)" but got "oklch(0 0 0)"
-[FAIL] e.style['color'] = "oklch(from oklab(0.7 45 30) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklab(0.7 45 30) l c h / alpha)" but got "oklch(0.7 54.0833 33.6901)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 0 0)" but got "oklch(0 0 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 0 0deg)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 0 0deg)" but got "oklch(0 0 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 0 0 / 0)" but got "oklch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 0 0deg / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 0 0deg / 0)" but got "oklch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 c h / alpha)" but got "oklch(0 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l 0 h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l 0 h / alpha)" but got "oklch(0.7 0 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c 0 / alpha)" but got "oklch(0.7 0.45 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c 0deg / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c 0deg / alpha)" but got "oklch(0.7 0.45 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h / 0)" but got "oklch(0.7 0.45 30 / 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 0 c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0 0.45 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 0 c h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l 0 h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l 0 h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 0 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c 0deg / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 0 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c 0deg / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c h / 0)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / 0)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c h / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0.25 c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0.25 c h / alpha)" but got "oklch(0.25 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l 0.25 h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l 0.25 h / alpha)" but got "oklch(0.7 0.25 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c 0.25 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c 0.25 / alpha)" but got "oklch(0.7 0.45 0.25)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c 25deg / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c 25deg / alpha)" but got "oklch(0.7 0.45 25)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h / 0.25)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h / 0.25)" but got "oklch(0.7 0.45 30 / 0.25)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 0.25 c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.25 0.45 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 0.25 c h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l 0.25 h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.25 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l 0.25 h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c 0.25 / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 0.25 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c 0.25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c 25deg / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 25 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c 25deg / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c h / 0.25)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / 0.25)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c h / 0.25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 2 3 400 / 500)" should set the property value
-  Colors do not match.\nActual:   oklch(1 3 40)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 2 3 400 / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) -2 -3 -400 / -500)" should set the property value
-  Colors do not match.\nActual:   oklch(0 0 320 / 0)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) -2 -3 -400 / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 0.5 1.2 400deg / 500)" should set the property value
-  Colors do not match.\nActual:   oklch(0.5 1.2 40)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 0.5 1.2 400deg / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 0.5 1.2 -400deg / -500)" should set the property value
-  Colors do not match.\nActual:   oklch(0.5 1.2 320 / 0)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 0.5 1.2 -400deg / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c c / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c c / alpha)" but got "oklch(0.7 0.45 0.45)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c c / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 0.45 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c c / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 2, expected 30 +/- 0.01, expected 30 but got 0.45
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) calc(l) calc(c) calc(h))" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) calc(l) calc(c) calc(h))" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) calc(l) calc(c) calc(h) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(   / ) calc(l) calc(c) calc(h) / calc(alpha))" but got "oklch(   / )"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.2 300) calc(l - 0.2) c h)" should set the property value
-  Colors do not match.\nActual:   oklch(0.5 0.2 300)\nExpected: oklch(from oklch(0.7 0.2 300) calc(-0.2 + l) c h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
+  Colors do not match.\nActual:   lch(from lch(50 100 300) l c calc(h * 2.5))\nExpected: lch(from lch(50 100 300) l c calc(2.5 * h)).\nError: assert_equals: Color format is correct. expected "lch(from lch(  ) l c calc( * h))" but got "lch(from lch(  ) l c calc(h * ))"
 [FAIL] e.style['color'] = "oklch(from oklch(0.7 0.2 300) l calc(c / 2) h)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.1 300)\nExpected: oklch(from oklch(0.7 0.2 300) l calc(0.5 * c) h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
+  Colors do not match.\nActual:   oklch(from oklch(0.7 0.2 300) l calc(c / 2) h)\nExpected: oklch(from oklch(0.7 0.2 300) l calc(0.5 * c) h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 0.5 +/- 0.01, expected 0.5 but got 2
 [FAIL] e.style['color'] = "oklch(from oklch(0.7 0.2 300) l c calc(h * 2.5))" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.2 30)\nExpected: oklch(from oklch(0.7 0.2 300) l c calc(2.5 * h)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) none none none)" but got "oklch(none none none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) none none none / none)" but got "oklch(none none none / none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c none)" but got "oklch(0.7 0.45 none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c none / alpha)" but got "oklch(0.7 0.45 none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h / none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h / none)" but got "oklch(0.7 0.45 30 / none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c none / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 none / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c h / none)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / none)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c h / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(none none none) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(none none none) l c h)" but got "oklch(0 0 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(none none none / none) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(none none none / none) l c h / alpha)" but got "oklch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 none 30) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 none 30) l c h)" but got "oklch(0.7 0 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / none) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30 / none) l c h / alpha)" but got "oklch(0.7 0.45 30 / 0)"
-[FAIL] e.style['color'] = "oklch(from color-mix(in oklch, oklch(0.7 0.45 30), oklch(0.7 0.45 30)) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from color-mix(in oklch, oklch(0.7 0.45 30), oklch(0.7 0.45 30)) l c h / alpha)" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b)" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / alpha)" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb    / ) srgb r g b)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb    / ) srgb r g b / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "color(from color(from color(srgb 0.7 0.5 0.3) srgb r g b) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(srgb 0.7 0.5 0.3) srgb r g b) srgb r g b)" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 0 0 0)" but got "color(srgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 0 0 0 / 0)" but got "color(srgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 0 g b / alpha)" but got "color(srgb 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r 0 b / alpha)" but got "color(srgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g 0 / alpha)" but got "color(srgb 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 0)" but got "color(srgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 0.2 g b / alpha)" but got "color(srgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 20% g b / alpha)" but got "color(srgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r 0.2 b / alpha)" but got "color(srgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r 20% b / alpha)" but got "color(srgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g 0.2 / alpha)" but got "color(srgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g 20% / alpha)" but got "color(srgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 0.2)" but got "color(srgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 20%)" but got "color(srgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 2 3 4)" but got "color(srgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 2 3 4 / 5)" but got "color(srgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb -2 -3 -4)" but got "color(srgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb -2 -3 -4 / -5)" but got "color(srgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 200% 300% 400%)" but got "color(srgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 200% 300% 400% / 500%)" but got "color(srgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb -200% -300% -400%)" but got "color(srgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb -200% -300% -400% / -500%)" but got "color(srgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb g b r)" but got "color(srgb 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb b alpha r / g)" but got "color(srgb 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r r r / r)" but got "color(srgb 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb alpha alpha alpha / alpha)" but got "color(srgb 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(srgb 1.7 1.5 1.3) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 1.7 1.5 1.3) srgb r g b)" but got "color(srgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 1.7 1.5 1.3) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 1.7 1.5 1.3) srgb r g b / alpha)" but got "color(srgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 1.7 1.5 1.3 / 140%) srgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1.7 1.5 1.3)\nExpected: color(from color(srgb 1.7 1.5 1.3) srgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb   ) srgb r g b)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "color(from color(srgb 1.7 1.5 1.3 / 140%) srgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1.7 1.5 1.3)\nExpected: color(from color(srgb 1.7 1.5 1.3) srgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb   ) srgb r g b / alpha)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "color(from color(srgb -0.7 -0.5 -0.3) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb -0.7 -0.5 -0.3) srgb r g b)" but got "color(srgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb -0.7 -0.5 -0.3) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb -0.7 -0.5 -0.3) srgb r g b / alpha)" but got "color(srgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb -0.7 -0.5 -0.3 / -40%) srgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(srgb -0.7 -0.5 -0.3 / 0) srgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb - - - / ) srgb r g b)" but got "color(srgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(srgb -0.7 -0.5 -0.3 / -40%) srgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(srgb -0.7 -0.5 -0.3 / 0) srgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb - - - / ) srgb r g b / alpha)" but got "color(srgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb calc(r) calc(g) calc(b))" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(srgb    / ) srgb calc(r) calc(g) calc(b) / calc(alpha))" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb none none none)" but got "color(srgb none none none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb none none none / none)" but got "color(srgb none none none / none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g none)" but got "color(srgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g none / alpha)" but got "color(srgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / none)" but got "color(srgb 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 none / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / none)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(srgb none none none) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb none none none) srgb r g b)" but got "color(srgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(srgb none none none / none) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb none none none / none) srgb r g b / alpha)" but got "color(srgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 none 0.3) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 none 0.3) srgb r g b)" but got "color(srgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / none) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3 / none) srgb r g b / alpha)" but got "color(srgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color-mix(in srgb, color(srgb 0.7 0.5 0.3), color(srgb 0.7 0.5 0.3)) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in srgb, color(srgb 0.7 0.5 0.3), color(srgb 0.7 0.5 0.3)) srgb r g b / alpha)" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b)" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / alpha)" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear    / ) srgb-linear r g b)" but got "color(srgb-linear    / )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear    / ) srgb-linear r g b / alpha)" but got "color(srgb-linear    / )"
-[FAIL] e.style['color'] = "color(from color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b) srgb-linear r g b)" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 0 0)" but got "color(srgb-linear 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 0 0 / 0)" but got "color(srgb-linear 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 g b / alpha)" but got "color(srgb-linear 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 0 b / alpha)" but got "color(srgb-linear 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 0 / alpha)" but got "color(srgb-linear 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 0)" but got "color(srgb-linear 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0.2 g b / alpha)" but got "color(srgb-linear 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 20% g b / alpha)" but got "color(srgb-linear 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 0.2 b / alpha)" but got "color(srgb-linear 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 20% b / alpha)" but got "color(srgb-linear 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 0.2 / alpha)" but got "color(srgb-linear 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 20% / alpha)" but got "color(srgb-linear 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 0.2)" but got "color(srgb-linear 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 20%)" but got "color(srgb-linear 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 2 3 4)" but got "color(srgb-linear 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 2 3 4 / 5)" but got "color(srgb-linear 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -2 -3 -4)" but got "color(srgb-linear -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -2 -3 -4 / -5)" but got "color(srgb-linear -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 200% 300% 400%)" but got "color(srgb-linear 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 200% 300% 400% / 500%)" but got "color(srgb-linear 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -200% -300% -400%)" but got "color(srgb-linear -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -200% -300% -400% / -500%)" but got "color(srgb-linear -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear g b r)" but got "color(srgb-linear 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear b alpha r / g)" but got "color(srgb-linear 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r r r / r)" but got "color(srgb-linear 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear alpha alpha alpha / alpha)" but got "color(srgb-linear 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b)" but got "color(srgb-linear 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b / alpha)" but got "color(srgb-linear 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 1.7 1.5 1.3 / 140%) srgb-linear r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 1.7 1.5 1.3)\nExpected: color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear   ) srgb-linear r g b)" but got "color(srgb-linear   )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 1.7 1.5 1.3 / 140%) srgb-linear r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 1.7 1.5 1.3)\nExpected: color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear   ) srgb-linear r g b / alpha)" but got "color(srgb-linear   )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear -0.7 -0.5 -0.3) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear -0.7 -0.5 -0.3) srgb-linear r g b)" but got "color(srgb-linear -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear -0.7 -0.5 -0.3) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear -0.7 -0.5 -0.3) srgb-linear r g b / alpha)" but got "color(srgb-linear -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear -0.7 -0.5 -0.3 / -40%) srgb-linear r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(srgb-linear -0.7 -0.5 -0.3 / 0) srgb-linear r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear - - - / ) srgb-linear r g b)" but got "color(srgb-linear - - - / )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear -0.7 -0.5 -0.3 / -40%) srgb-linear r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(srgb-linear -0.7 -0.5 -0.3 / 0) srgb-linear r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear - - - / ) srgb-linear r g b / alpha)" but got "color(srgb-linear - - - / )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear calc(r) calc(g) calc(b))" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear    / ) srgb-linear calc(r) calc(g) calc(b) / calc(alpha))" but got "color(srgb-linear    / )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear none none none)" but got "color(srgb-linear none none none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear none none none / none)" but got "color(srgb-linear none none none / none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g none)" but got "color(srgb-linear 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g none / alpha)" but got "color(srgb-linear 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / none)" but got "color(srgb-linear 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 none / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / none)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(srgb-linear none none none) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear none none none) srgb-linear r g b)" but got "color(srgb-linear 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear none none none / none) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear none none none / none) srgb-linear r g b / alpha)" but got "color(srgb-linear 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 none 0.3) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 none 0.3) srgb-linear r g b)" but got "color(srgb-linear 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / none) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3 / none) srgb-linear r g b / alpha)" but got "color(srgb-linear 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color-mix(in srgb-linear, color(srgb-linear 0.7 0.5 0.3), color(srgb-linear 0.7 0.5 0.3)) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in srgb-linear, color(srgb-linear 0.7 0.5 0.3), color(srgb-linear 0.7 0.5 0.3)) srgb-linear r g b / alpha)" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b)" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / alpha)" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb    / ) a-rgb r g b)" but got "color(a-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb    / ) a-rgb r g b / alpha)" but got "color(a-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b) a98-rgb r g b)" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 0 0)" but got "color(a98-rgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 0 0 / 0)" but got "color(a98-rgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 g b / alpha)" but got "color(a98-rgb 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 0 b / alpha)" but got "color(a98-rgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 0 / alpha)" but got "color(a98-rgb 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 0)" but got "color(a98-rgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0.2 g b / alpha)" but got "color(a98-rgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 20% g b / alpha)" but got "color(a98-rgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 0.2 b / alpha)" but got "color(a98-rgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 20% b / alpha)" but got "color(a98-rgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 0.2 / alpha)" but got "color(a98-rgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 20% / alpha)" but got "color(a98-rgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 0.2)" but got "color(a98-rgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 20%)" but got "color(a98-rgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 2 3 4)" but got "color(a98-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 2 3 4 / 5)" but got "color(a98-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -2 -3 -4)" but got "color(a98-rgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -2 -3 -4 / -5)" but got "color(a98-rgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 200% 300% 400%)" but got "color(a98-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 200% 300% 400% / 500%)" but got "color(a98-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -200% -300% -400%)" but got "color(a98-rgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -200% -300% -400% / -500%)" but got "color(a98-rgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb g b r)" but got "color(a98-rgb 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb b alpha r / g)" but got "color(a98-rgb 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r r r / r)" but got "color(a98-rgb 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb alpha alpha alpha / alpha)" but got "color(a98-rgb 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb g b r)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b)" but got "color(a98-rgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b / alpha)" but got "color(a98-rgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 1.7 1.5 1.3 / 140%) a98-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 1.7 1.5 1.3)\nExpected: color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb   ) a-rgb r g b)" but got "color(a-rgb   )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 1.7 1.5 1.3 / 140%) a98-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 1.7 1.5 1.3)\nExpected: color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb   ) a-rgb r g b / alpha)" but got "color(a-rgb   )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb -0.7 -0.5 -0.3) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb -0.7 -0.5 -0.3) a98-rgb r g b)" but got "color(a98-rgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb -0.7 -0.5 -0.3) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb -0.7 -0.5 -0.3) a98-rgb r g b / alpha)" but got "color(a98-rgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb -0.7 -0.5 -0.3 / -40%) a98-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(a98-rgb -0.7 -0.5 -0.3 / 0) a98-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb - - - / ) a-rgb r g b)" but got "color(a-rgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb -0.7 -0.5 -0.3 / -40%) a98-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(a98-rgb -0.7 -0.5 -0.3 / 0) a98-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb - - - / ) a-rgb r g b / alpha)" but got "color(a-rgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb calc(r) calc(g) calc(b))" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb    / ) a-rgb calc(r) calc(g) calc(b) / calc(alpha))" but got "color(a-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb none none none)" but got "color(a98-rgb none none none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb none none none / none)" but got "color(a98-rgb none none none / none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g none)" but got "color(a98-rgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g none / alpha)" but got "color(a98-rgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / none)" but got "color(a98-rgb 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 none / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / none)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(a98-rgb none none none) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb none none none) a98-rgb r g b)" but got "color(a98-rgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb none none none / none) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb none none none / none) a98-rgb r g b / alpha)" but got "color(a98-rgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 none 0.3) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 none 0.3) a98-rgb r g b)" but got "color(a98-rgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / none) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3 / none) a98-rgb r g b / alpha)" but got "color(a98-rgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color-mix(in a98-rgb, color(a98-rgb 0.7 0.5 0.3), color(a98-rgb 0.7 0.5 0.3)) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in a98-rgb, color(a98-rgb 0.7 0.5 0.3), color(a98-rgb 0.7 0.5 0.3)) a98-rgb r g b / alpha)" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b)" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / alpha)" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(rec    / ) rec r g b)" but got "color(rec    / )"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(rec    / ) rec r g b / alpha)" but got "color(rec    / )"
-[FAIL] e.style['color'] = "color(from color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b) rec2020 r g b)" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 0 0)" but got "color(rec2020 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 0 0 / 0)" but got "color(rec2020 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 g b / alpha)" but got "color(rec2020 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 0 b / alpha)" but got "color(rec2020 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 0 / alpha)" but got "color(rec2020 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 0)" but got "color(rec2020 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 0.2 g b / alpha)" but got "color(rec2020 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 20% g b / alpha)" but got "color(rec2020 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 0.2 b / alpha)" but got "color(rec2020 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 20% b / alpha)" but got "color(rec2020 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 0.2 / alpha)" but got "color(rec2020 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 20% / alpha)" but got "color(rec2020 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 0.2)" but got "color(rec2020 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 20%)" but got "color(rec2020 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 2 3 4)" but got "color(rec2020 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 2 3 4 / 5)" but got "color(rec2020 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 -2 -3 -4)" but got "color(rec2020 -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 -2 -3 -4 / -5)" but got "color(rec2020 -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 200% 300% 400%)" but got "color(rec2020 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 200% 300% 400% / 500%)" but got "color(rec2020 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 -200% -300% -400%)" but got "color(rec2020 -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 -200% -300% -400% / -500%)" but got "color(rec2020 -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 g b r)" but got "color(rec2020 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 b alpha r / g)" but got "color(rec2020 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r r r / r)" but got "color(rec2020 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 alpha alpha alpha / alpha)" but got "color(rec2020 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 g b r)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b)" but got "color(rec2020 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b / alpha)" but got "color(rec2020 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 1.7 1.5 1.3 / 140%) rec2020 r g b)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 1.7 1.5 1.3)\nExpected: color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(rec   ) rec r g b)" but got "color(rec   )"
-[FAIL] e.style['color'] = "color(from color(rec2020 1.7 1.5 1.3 / 140%) rec2020 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 1.7 1.5 1.3)\nExpected: color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(rec   ) rec r g b / alpha)" but got "color(rec   )"
-[FAIL] e.style['color'] = "color(from color(rec2020 -0.7 -0.5 -0.3) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 -0.7 -0.5 -0.3) rec2020 r g b)" but got "color(rec2020 -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 -0.7 -0.5 -0.3) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 -0.7 -0.5 -0.3) rec2020 r g b / alpha)" but got "color(rec2020 -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 -0.7 -0.5 -0.3 / -40%) rec2020 r g b)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(rec2020 -0.7 -0.5 -0.3 / 0) rec2020 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(rec - - - / ) rec r g b)" but got "color(rec - - - / )"
-[FAIL] e.style['color'] = "color(from color(rec2020 -0.7 -0.5 -0.3 / -40%) rec2020 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(rec2020 -0.7 -0.5 -0.3 / 0) rec2020 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(rec - - - / ) rec r g b / alpha)" but got "color(rec - - - / )"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 calc(r) calc(g) calc(b))" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(rec    / ) rec calc(r) calc(g) calc(b) / calc(alpha))" but got "color(rec    / )"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 none none none)" but got "color(rec2020 none none none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 none none none / none)" but got "color(rec2020 none none none / none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g none)" but got "color(rec2020 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g none / alpha)" but got "color(rec2020 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / none)" but got "color(rec2020 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 none / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / none)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(rec2020 none none none) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 none none none) rec2020 r g b)" but got "color(rec2020 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 none none none / none) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 none none none / none) rec2020 r g b / alpha)" but got "color(rec2020 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 none 0.3) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 none 0.3) rec2020 r g b)" but got "color(rec2020 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / none) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3 / none) rec2020 r g b / alpha)" but got "color(rec2020 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color-mix(in rec2020, color(rec2020 0.7 0.5 0.3), color(rec2020 0.7 0.5 0.3)) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in rec2020, color(rec2020 0.7 0.5 0.3), color(rec2020 0.7 0.5 0.3)) rec2020 r g b / alpha)" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b)" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb    / ) prophoto-rgb r g b)" but got "color(prophoto-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb    / ) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b) prophoto-rgb r g b)" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 0 0)" but got "color(prophoto-rgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 0 0 / 0)" but got "color(prophoto-rgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 g b / alpha)" but got "color(prophoto-rgb 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 0 b / alpha)" but got "color(prophoto-rgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 0 / alpha)" but got "color(prophoto-rgb 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 0)" but got "color(prophoto-rgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0.2 g b / alpha)" but got "color(prophoto-rgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 20% g b / alpha)" but got "color(prophoto-rgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 0.2 b / alpha)" but got "color(prophoto-rgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 20% b / alpha)" but got "color(prophoto-rgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 0.2 / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 20% / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 0.2)" but got "color(prophoto-rgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 20%)" but got "color(prophoto-rgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 2 3 4)" but got "color(prophoto-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 2 3 4 / 5)" but got "color(prophoto-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -2 -3 -4)" but got "color(prophoto-rgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -2 -3 -4 / -5)" but got "color(prophoto-rgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 200% 300% 400%)" but got "color(prophoto-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 200% 300% 400% / 500%)" but got "color(prophoto-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -200% -300% -400%)" but got "color(prophoto-rgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -200% -300% -400% / -500%)" but got "color(prophoto-rgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb g b r)" but got "color(prophoto-rgb 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb b alpha r / g)" but got "color(prophoto-rgb 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r r r / r)" but got "color(prophoto-rgb 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb alpha alpha alpha / alpha)" but got "color(prophoto-rgb 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb g b r)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b)" but got "color(prophoto-rgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 1.7 1.5 1.3 / 140%) prophoto-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 1.7 1.5 1.3)\nExpected: color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb   ) prophoto-rgb r g b)" but got "color(prophoto-rgb   )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 1.7 1.5 1.3 / 140%) prophoto-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 1.7 1.5 1.3)\nExpected: color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb   ) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb   )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb -0.7 -0.5 -0.3) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb -0.7 -0.5 -0.3) prophoto-rgb r g b)" but got "color(prophoto-rgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb -0.7 -0.5 -0.3) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb -0.7 -0.5 -0.3) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb -0.7 -0.5 -0.3 / -40%) prophoto-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(prophoto-rgb -0.7 -0.5 -0.3 / 0) prophoto-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb - - - / ) prophoto-rgb r g b)" but got "color(prophoto-rgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb -0.7 -0.5 -0.3 / -40%) prophoto-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(prophoto-rgb -0.7 -0.5 -0.3 / 0) prophoto-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb - - - / ) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb calc(r) calc(g) calc(b))" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb    / ) prophoto-rgb calc(r) calc(g) calc(b) / calc(alpha))" but got "color(prophoto-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb none none none)" but got "color(prophoto-rgb none none none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb none none none / none)" but got "color(prophoto-rgb none none none / none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g none)" but got "color(prophoto-rgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g none / alpha)" but got "color(prophoto-rgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / none)" but got "color(prophoto-rgb 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 none / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / none)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb none none none) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb none none none) prophoto-rgb r g b)" but got "color(prophoto-rgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb none none none / none) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb none none none / none) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 none 0.3) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 none 0.3) prophoto-rgb r g b)" but got "color(prophoto-rgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / none) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3 / none) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color-mix(in prophoto-rgb, color(prophoto-rgb 0.7 0.5 0.3), color(prophoto-rgb 0.7 0.5 0.3)) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in prophoto-rgb, color(prophoto-rgb 0.7 0.5 0.3), color(prophoto-rgb 0.7 0.5 0.3)) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b)" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / alpha)" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(display-p    / ) display-p r g b)" but got "color(display-p    / )"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(display-p    / ) display-p r g b / alpha)" but got "color(display-p    / )"
-[FAIL] e.style['color'] = "color(from color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b) display-p3 r g b)" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 0 0)" but got "color(display-p3 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 0 0 / 0)" but got "color(display-p3 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 g b / alpha)" but got "color(display-p3 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 0 b / alpha)" but got "color(display-p3 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 0 / alpha)" but got "color(display-p3 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 0)" but got "color(display-p3 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 0.2 g b / alpha)" but got "color(display-p3 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 20% g b / alpha)" but got "color(display-p3 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 0.2 b / alpha)" but got "color(display-p3 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 20% b / alpha)" but got "color(display-p3 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 0.2 / alpha)" but got "color(display-p3 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 20% / alpha)" but got "color(display-p3 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 0.2)" but got "color(display-p3 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 20%)" but got "color(display-p3 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 2 3 4)" but got "color(display-p3 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 2 3 4 / 5)" but got "color(display-p3 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 -2 -3 -4)" but got "color(display-p3 -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 -2 -3 -4 / -5)" but got "color(display-p3 -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 200% 300% 400%)" but got "color(display-p3 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 200% 300% 400% / 500%)" but got "color(display-p3 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 -200% -300% -400%)" but got "color(display-p3 -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 -200% -300% -400% / -500%)" but got "color(display-p3 -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 g b r)" but got "color(display-p3 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 b alpha r / g)" but got "color(display-p3 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r r r / r)" but got "color(display-p3 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 alpha alpha alpha / alpha)" but got "color(display-p3 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 g b r)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b)" but got "color(display-p3 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b / alpha)" but got "color(display-p3 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 1.7 1.5 1.3 / 140%) display-p3 r g b)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 1.7 1.5 1.3)\nExpected: color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(display-p   ) display-p r g b)" but got "color(display-p   )"
-[FAIL] e.style['color'] = "color(from color(display-p3 1.7 1.5 1.3 / 140%) display-p3 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 1.7 1.5 1.3)\nExpected: color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(display-p   ) display-p r g b / alpha)" but got "color(display-p   )"
-[FAIL] e.style['color'] = "color(from color(display-p3 -0.7 -0.5 -0.3) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 -0.7 -0.5 -0.3) display-p3 r g b)" but got "color(display-p3 -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 -0.7 -0.5 -0.3) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 -0.7 -0.5 -0.3) display-p3 r g b / alpha)" but got "color(display-p3 -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 -0.7 -0.5 -0.3 / -40%) display-p3 r g b)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(display-p3 -0.7 -0.5 -0.3 / 0) display-p3 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(display-p - - - / ) display-p r g b)" but got "color(display-p - - - / )"
-[FAIL] e.style['color'] = "color(from color(display-p3 -0.7 -0.5 -0.3 / -40%) display-p3 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(display-p3 -0.7 -0.5 -0.3 / 0) display-p3 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(display-p - - - / ) display-p r g b / alpha)" but got "color(display-p - - - / )"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 calc(r) calc(g) calc(b))" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(display-p    / ) display-p calc(r) calc(g) calc(b) / calc(alpha))" but got "color(display-p    / )"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 none none none)" but got "color(display-p3 none none none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 none none none / none)" but got "color(display-p3 none none none / none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g none)" but got "color(display-p3 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g none / alpha)" but got "color(display-p3 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / none)" but got "color(display-p3 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 none / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / none)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(display-p3 none none none) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 none none none) display-p3 r g b)" but got "color(display-p3 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 none none none / none) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 none none none / none) display-p3 r g b / alpha)" but got "color(display-p3 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 none 0.3) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 none 0.3) display-p3 r g b)" but got "color(display-p3 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / none) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3 / none) display-p3 r g b / alpha)" but got "color(display-p3 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color-mix(in display-p3, color(display-p3 0.7 0.5 0.3), color(display-p3 0.7 0.5 0.3)) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in display-p3, color(display-p3 0.7 0.5 0.3), color(display-p3 0.7 0.5 0.3)) display-p3 r g b / alpha)" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / alpha)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z / alpha)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(from color(xyz 7 -20.5 100) xyz x y z) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(from color(xyz-d  - ) xyz-d x y z) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0 / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.2 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0.2 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0.2).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / 20%)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 20 +/- 0.01, expected 20 but got 0.2
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.2 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0.2 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0.2 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 -20.5 100 7)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 7 7)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected -20.5 +/- 0.01, expected -20.5 but got 7
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 -20.5 100 7 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 7 7)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz calc(x) calc(y) calc(z))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 calc(x) calc(y) calc(z)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d calc(x) calc(y) calc(z))" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz calc(x) calc(y) calc(z) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 calc(x) calc(y) calc(z) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d calc(x) calc(y) calc(z) / calc(alpha))" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz none none none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 none none none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 none none none / none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / none).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / none)" but got "color(xyz-d  -  / none)"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / none)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz none none none) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0)\nExpected: color(from color(xyz-d65 none none none) xyz-d65 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "color(from color(xyz none none none / none) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0 / 0)\nExpected: color(from color(xyz-d65 none none none / none) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 none 100) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100)\nExpected: color(from color(xyz-d65 7 none 100) xyz-d65 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / none) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100 / none) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 4
-[FAIL] e.style['color'] = "color(from color-mix(in xyz, color(xyz 0.7 0.5 0.3), color(xyz 0.7 0.5 0.3)) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.7 0.5 0.3)\nExpected: color(from color-mix(in xyz-d65, color(xyz-d65 0.7 0.5 0.3), color(xyz-d65 0.7 0.5 0.3)) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / alpha)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z / alpha)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100)\nExpected: color(from color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z) xyz-d50 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(from color(xyz-d  - ) xyz-d x y z) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 0 0)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 0 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 0 0 / 0)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 0 0 / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 0)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 0).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 0 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0.2 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0.2 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 0.2)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 0.2).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 20%)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 20 +/- 0.01, expected 20 but got 0.2
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0.2 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0.2 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 0.2 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 -20.5 100 7)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 7 7)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected -20.5 +/- 0.01, expected -20.5 but got 7
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 -20.5 100 7 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 7 7)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 calc(x) calc(y) calc(z))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 calc(x) calc(y) calc(z)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d calc(x) calc(y) calc(z))" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 calc(x) calc(y) calc(z) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 calc(x) calc(y) calc(z) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d calc(x) calc(y) calc(z) / calc(alpha))" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 none none none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 none none none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 none none none / none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / none).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / none)" but got "color(xyz-d  -  / none)"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 none / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / none)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 none none none) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 0 0)\nExpected: color(from color(xyz-d50 none none none) xyz-d50 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 none none none / none) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 0 0 / 0)\nExpected: color(from color(xyz-d50 none none none / none) xyz-d50 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 none 100) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0 100)\nExpected: color(from color(xyz-d50 7 none 100) xyz-d50 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / none) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d50 7 -20.5 100 / none) xyz-d50 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 4
-[FAIL] e.style['color'] = "color(from color-mix(in xyz-d50, color(xyz-d50 0.7 0.5 0.3), color(xyz-d50 0.7 0.5 0.3)) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0.7 0.5 0.3)\nExpected: color(from color-mix(in xyz-d50, color(xyz-d50 0.7 0.5 0.3), color(xyz-d50 0.7 0.5 0.3)) xyz-d50 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / alpha)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z / alpha)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(from color(xyz-d  - ) xyz-d x y z) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0 / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.2 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0.2 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0.2).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 20%)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 20 +/- 0.01, expected 20 but got 0.2
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.2 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0.2 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0.2 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 -20.5 100 7)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 7 7)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected -20.5 +/- 0.01, expected -20.5 but got 7
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 -20.5 100 7 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 7 7)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 calc(x) calc(y) calc(z))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 calc(x) calc(y) calc(z)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d calc(x) calc(y) calc(z))" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 calc(x) calc(y) calc(z) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 calc(x) calc(y) calc(z) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d calc(x) calc(y) calc(z) / calc(alpha))" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 none none none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 none none none / none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / none).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / none)" but got "color(xyz-d  -  / none)"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / none)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 none none none) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0)\nExpected: color(from color(xyz-d65 none none none) xyz-d65 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 none none none / none) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0 / 0)\nExpected: color(from color(xyz-d65 none none none / none) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 none 100) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100)\nExpected: color(from color(xyz-d65 7 none 100) xyz-d65 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / none) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100 / none) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 4
-[FAIL] e.style['color'] = "color(from color-mix(in xyz-d65, color(xyz-d65 0.7 0.5 0.3), color(xyz-d65 0.7 0.5 0.3)) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.7 0.5 0.3)\nExpected: color(from color-mix(in xyz-d65, color(xyz-d65 0.7 0.5 0.3), color(xyz-d65 0.7 0.5 0.3)) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "rgb(from indianred 255 g b)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(from indianred 255 g b)" but got "color(srgb 1 0.360784 0.360784)"
+  Colors do not match.\nActual:   oklch(from oklch(0.7 0.2 300) l c calc(h * 2.5))\nExpected: oklch(from oklch(0.7 0.2 300) l c calc(2.5 * h)).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(  ) l c calc( * h))" but got "oklch(from oklch(  ) l c calc(h * ))"
 [FAIL] e.style['color'] = "lch(from peru calc(l * 0.8) c h)" should set the property value
-  Colors do not match.\nActual:   lch(49.7972 54.0177 63.6639)\nExpected: lch(from peru calc(0.8 * l) c h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
+  Colors do not match.\nActual:   lch(from peru calc(l * 0.8) c h)\nExpected: lch(from peru calc(0.8 * l) c h).\nError: assert_equals: Color format is correct. expected "lch(from peru calc( * l) c h)" but got "lch(from peru calc(l * ) c h)"
+[FAIL] e.style['color'] = "color(from rebeccapurple srgb r g b)" should set the property value
+  Colors do not match.\nActual:   color(from rebeccapurple srgb r g b)\nExpected: color(srgb 0.4 0.2 0.6).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
+[FAIL] e.style['color'] = "rgb(from color(srgb 0.4 0.2 0.6) r g b)" should set the property value
+  Colors do not match.\nActual:   rgb(from color(srgb 0.4 0.2 0.6) r g b)\nExpected: color(srgb 0.4 0.2 0.6).\nError: assert_equals: Color format is correct. expected "color(srgb   )" but got "rgb(from color(srgb   ) r g b)"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-one-sets-flex-basis-to-zero-px-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-one-sets-flex-basis-to-zero-px-expected.txt
deleted file mode 100644
index 6f3c4ed..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-one-sets-flex-basis-to-zero-px-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] .flexbox 6
-  assert_equals: \n<div class="flexbox column">\n  <div class="flex-half-one-zero-px" data-expected-height="4">Flex item with flex: 0.5 1 0px</div>\n</div>\nheight expected 4 but got 0
-[FAIL] .flexbox 9
-  assert_equals: \n<div class="flexbox column">\n  <div class="flex-one-one-zero-px" data-expected-height="14">Flex item with flex: 1 1 0px</div>\n</div>\nheight expected 14 but got 0
-[FAIL] .flexbox 15
-  assert_equals: \n<div class="flexbox column vertical">\n  <div class="flex-half-one-zero-px" data-expected-width="4">Flex item with flex: 0.5 1 0px</div>\n</div>\nwidth expected 4 but got 0
-[FAIL] .flexbox 18
-  assert_equals: \n<div class="flexbox column vertical">\n  <div class="flex-one-one-zero-px" data-expected-width="14">Flex item with flex: 1 1 0px</div>\n</div>\nwidth expected 14 but got 0
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-one-sets-flex-basis-to-zero-px.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-one-sets-flex-basis-to-zero-px.html
index d711686..1f11f3e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-one-sets-flex-basis-to-zero-px.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-one-sets-flex-basis-to-zero-px.html
@@ -76,13 +76,15 @@
   <div class="flex-half-one-zero-percent" data-expected-height="14">Flex item with flex: 0.5 1 0%</div>
 </div>
 
+<!-- Following comment follows current spec to the letter, but it is not web
+     compatible. The expectation is for what everyone is shipping. -->
 <!-- A flex-grow of 0 would size the container to the flex base size of the item (0px),
      and a flex-grow of 1 would size it to the max-content contribution of the item (14px).
      Therefore, a flew-grow of 0.5 sizes the container to the average, 7px.
      And then the item grows to fill half of that, 3.5px.
      Note that Gecko, Blink and WebKit use the flex-basis instead. -->
 <div class="flexbox column">
-  <div class="flex-half-one-zero-px" data-expected-height="4">Flex item with flex: 0.5 1 0px</div>
+  <div class="flex-half-one-zero-px" data-expected-height="0">Flex item with flex: 0.5 1 0px</div>
 </div>
 
 <div class="flexbox column">
@@ -93,10 +95,12 @@
   <div class="flex-one-one-zero-percent" data-expected-height="14">Flex item with flex: 1 1 0%</div>
 </div>
 
+<!-- Following comment follows current spec to the letter, but it is not web
+     compatible. The expectation is for what everyone is shipping. -->
 <!-- flex-grow is >= 1, so the flex container is sized to the max-content contribution of the item.
      Note that Gecko, Blink and WebKit use the flex-basis instead. -->
 <div class="flexbox column">
-  <div class="flex-one-one-zero-px" data-expected-height="14">Flex item with flex: 1 1 0px</div>
+  <div class="flex-one-one-zero-px" data-expected-height="0">Flex item with flex: 1 1 0px</div>
 </div>
 
 <div class="flexbox column vertical">
@@ -119,13 +123,15 @@
   <div class="flex-half-one-zero-percent" data-expected-width="14">Flex item with flex: 0.5 1 0%</div>
 </div>
 
+<!-- Following comment follows current spec to the letter, but it is not web
+     compatible. The expectation is for what everyone is shipping. -->
 <!-- A flex-grow of 0 would size the container to the flex base size of the item (0px),
      and a flex-grow of 1 would size it to the max-content contribution of the item (14px).
      Therefore, a flew-grow of 0.5 sizes the container to the average, 7px.
      And then the item grows to fill half of that, 3.5px.
      Note that Gecko, Blink and WebKit use the flex-basis instead. -->
 <div class="flexbox column vertical">
-  <div class="flex-half-one-zero-px" data-expected-width="4">Flex item with flex: 0.5 1 0px</div>
+  <div class="flex-half-one-zero-px" data-expected-width="0">Flex item with flex: 0.5 1 0px</div>
 </div>
 
 <div class="flexbox column vertical">
@@ -136,10 +142,12 @@
   <div class="flex-one-one-zero-percent" data-expected-width="14">Flex item with flex: 1 1 0%</div>
 </div>
 
+<!-- Following comment follows current spec to the letter, but it is not web
+     compatible. The expectation is for what everyone is shipping. -->
 <!-- flex-grow is >= 1, so the flex container is sized to the max-content contribution of the item.
      Note that Gecko, Blink and WebKit use the flex-basis instead. -->
 <div class="flexbox column vertical">
-  <div class="flex-one-one-zero-px" data-expected-width="14">Flex item with flex: 1 1 0px</div>
+  <div class="flex-one-one-zero-px" data-expected-width="0">Flex item with flex: 1 1 0px</div>
 </div>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-computed.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-computed.html
new file mode 100644
index 0000000..89854a6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-computed.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Masonry: masonry-slack getComputedStyle()</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<meta name="assert" content="masonry-slack computed value is as specified.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<style>
+  #target {
+    font-size: 40px;
+  }
+</style>
+<script>
+test_computed_value("masonry-slack", "normal");
+
+test_computed_value("masonry-slack", "10px");
+test_computed_value("masonry-slack", "20%");
+test_computed_value("masonry-slack", "calc(20% + 10px)");
+
+test_computed_value("masonry-slack", "calc(-0.5em + 10px)", "0px");
+test_computed_value("masonry-slack", "calc(0.5em + 10px)", "30px");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-invalid.html
new file mode 100644
index 0000000..feb90fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-invalid.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Masonry: masonry-slack parsing</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<meta name="assert" content="masonry-slack supports only the grammar 'normal | <length-percentage>'.">
+<meta name="assert" content="masonry-slack rejects negative <length-percentage>.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("masonry-slack", "auto");
+
+test_invalid_value("masonry-slack", "10");
+test_invalid_value("masonry-slack", "10px 20px");
+test_invalid_value("masonry-slack", "-1px");
+test_invalid_value("masonry-slack", "-10%");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-valid.html b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-valid.html
new file mode 100644
index 0000000..9447b9b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-masonry/tentative/parsing/masonry-slack-valid.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Masonry: masonry-slack parsing</title>
+<link rel="help" href="https://tabatkins.github.io/specs/css-masonry/">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<meta name="assert" content="masonry-slack supports the full grammar 'normal | <length-percentage>'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("masonry-slack", "normal");
+
+test_valid_value("masonry-slack", "0", "0px");
+test_valid_value("masonry-slack", "1px");
+test_valid_value("masonry-slack", "calc(2em + 3ex)");
+test_valid_value("masonry-slack", "4%");
+test_valid_value("masonry-slack", "5vmin");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-001.html b/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-001.html
new file mode 100644
index 0000000..eb9a620
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-001.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#computing-the-table-height">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1917028">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="This test verifies that the table's content height matches its final cross-size, and the table cells do not re-resolve their percentage heights based on the table's content height.">
+<style>
+.flex {
+  display: flex;
+}
+table {
+  border-spacing: 0;
+  width: 100px;
+  background: green;
+}
+tr {
+  height: 50px;
+}
+td {
+  height: 100%;
+}
+</style>
+
+<p>Test passes if there is a filled green square.</p>
+<div class="flex">
+  <table>
+    <thead><tr><td></td></tr></thead>
+    <tbody><tr><td></td></tr></tbody>
+  </table>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-002.html b/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-002.html
new file mode 100644
index 0000000..7b7556c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-002.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#computing-the-table-height">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1917028">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="The test verifies that the table's specified height matches its final main size, and the table cells do not re-resolve their percentage heights based on the table's height.">
+<style>
+.flex {
+  display: flex;
+  flex-direction: column;
+}
+table {
+  border-spacing: 0;
+  width: 100px;
+  height: 100px;
+  flex: none;
+  background: green;
+}
+tr {
+  height: 50px;
+}
+td {
+  height: 100%;
+}
+</style>
+
+<p>Test passes if there is a filled green square.</p>
+<div class="flex">
+  <table>
+    <thead><tr><td></td></tr></thead>
+    <tbody><tr><td></td></tr></tbody>
+  </table>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-003.html b/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-003.html
new file mode 100644
index 0000000..c8bfa9a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-003.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#computing-the-table-height">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1917028">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="The test verifies that the table's content height matches its final main size, and the table cells do not re-resolve their percentage heights based on the table's content height.">
+<style>
+.flex {
+  display: flex;
+  flex-direction: column;
+}
+table {
+  border-spacing: 0;
+  width: 100px;
+  background: green;
+}
+tr {
+  height: 50px;
+}
+td {
+  height: 100%;
+}
+</style>
+
+<p>Test passes if there is a filled green square.</p>
+<div class="flex">
+  <table>
+    <thead><tr><td></td></tr></thead>
+    <tbody><tr><td></td></tr></tbody>
+  </table>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-004.html b/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-004.html
new file mode 100644
index 0000000..a716ed2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/table-as-item-cell-percentage-004.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#computing-the-table-height">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1917028">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<meta name="assert" content="This test verifies that the table grid item's content height matches its final height, and the table cells do not re-resolve their percentage heights based on the table's content height.">
+<style>
+.grid {
+  display: grid;
+}
+table {
+  border-spacing: 0;
+  width: 100px;
+  background: green;
+}
+tr {
+  height: 50px;
+}
+td {
+  height: 100%;
+}
+</style>
+
+<p>Test passes if there is a filled green square.</p>
+<div class="grid">
+  <table>
+    <thead><tr><td></td></tr></thead>
+    <tbody><tr><td></td></tr></tbody>
+  </table>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/br-tag-not-added-with-inline-root-editable-element.html b/third_party/blink/web_tests/external/wpt/editing/other/br-tag-not-added-with-inline-root-editable-element.html
new file mode 100644
index 0000000..7edf1596
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/editing/other/br-tag-not-added-with-inline-root-editable-element.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+    <div id="target"><span contenteditable="true">D</span></div>
+</body>
+<script>
+    test(() => {
+        let selection = getSelection();
+        let range = document.createRange();
+        let element = document.getElementById("target").firstChild;
+        range.setStartAfter(element.firstChild);
+        range.setEndAfter(element.firstChild);
+        selection.removeAllRanges();
+        selection.addRange(range);
+        document.execCommand("delete");
+        const expectedHTML = "<span contenteditable=\"true\"></span>";
+        assert_not_equals(element.firstChild && element.firstChild.tagName, "BR", "First child is not a <br> tag");
+        assert_equals(element.outerHTML, expectedHTML, "HTML content matches the expected structure after delete");
+    }, "BR tag is not inserted after deleting the text node content since root editable element is inline");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/editing/other/insert-br-tag-on-text-node-removal-with-non-editable-elements.html b/third_party/blink/web_tests/external/wpt/editing/other/insert-br-tag-on-text-node-removal-with-non-editable-elements.html
new file mode 100644
index 0000000..47c0ab8d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/editing/other/insert-br-tag-on-text-node-removal-with-non-editable-elements.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+    <div contenteditable="true" id="target1">A<div contenteditable="false">can't edit</div></div>
+    <div contenteditable="true" id="target2"><div contenteditable="false">can't edit</div>B<div contenteditable="false">can't edit</div></div>
+    <div contenteditable="true" id="target3"><div contenteditable="false">can't edit</div>C</div>
+</body>
+
+<script>
+    test(() => {
+        let selection = getSelection();
+        let range = document.createRange();
+        let element = document.getElementById("target1");
+        range.selectNode(element.firstChild);
+        selection.removeAllRanges();
+        selection.addRange(range);
+        document.execCommand("delete");
+        const expectedHTML = "<br><div contenteditable=\"false\">can't edit</div>";
+        assert_equals(element.firstChild.tagName, "BR", "First child is a <br> tag");
+        assert_equals(element.innerHTML, expectedHTML, "HTML content matches the expected structure after delete");
+    }, "BR tag is inserted after deleting the text node present before non editable child element");
+
+    test(() => {
+        let selection = getSelection();
+        let range = document.createRange();
+        let element = document.getElementById("target2");
+        let textNode = element.childNodes[1];
+        range.setStartAfter(textNode);
+        range.setEndAfter(textNode);
+        selection.removeAllRanges();
+        selection.addRange(range);
+        document.execCommand("delete");
+        const expectedHTML = "<div contenteditable=\"false\">can't edit</div><br><div contenteditable=\"false\">can\'t edit</div>";
+        assert_equals(element.innerHTML, expectedHTML, "HTML content matches the expected structure after delete");
+    }, "BR tag is inserted after deleting the text node present between two non editable child elements");
+
+    test(() => {
+        let selection = getSelection();
+        let range = document.createRange();
+        let element = document.getElementById("target3");
+        let textNode = element.childNodes[1];
+        range.setStartAfter(textNode);
+        range.setEndAfter(textNode);
+        selection.removeAllRanges();
+        selection.addRange(range);
+        document.execCommand("delete");
+        const expectedHTML = "<div contenteditable=\"false\">can't edit</div><br>";
+        assert_equals(element.innerHTML, expectedHTML, "HTML content matches the expected structure after delete");
+    }, "BR tag is inserted after deleting the text node present after non editable child element");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window.js
index fbd75bad..457533f 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window.js
@@ -6,7 +6,8 @@
 // META: script=/common/subset-tests.js
 // META: timeout=long
 // META: variant=?1-4
-// META: variant=?5-last
+// META: variant=?5-8
+// META: variant=?9-12
 
 // These tests focus on the navigator.getInterestGroupAdAuctionData() method.
 
@@ -30,6 +31,71 @@
   assert_true(result.request.length === 0);
 }, 'getInterestGroupAdAuctionData() with no interest groups returns a zero length result.');
 
+async function testInvalidConfig(test, configObj, desc) {
+  await promise_rejects_js(
+      test, TypeError, navigator.getInterestGroupAdAuctionData(configObj),
+      desc);
+}
+
+subsetTest(promise_test, async test => {
+  await testInvalidConfig(test, {}, 'no seller');
+  await testInvalidConfig(test, {seller: 'example'}, 'invalid seller 1');
+  await testInvalidConfig(
+      test, {seller: 'http://example.org'}, 'invalid seller 2');
+  await testInvalidConfig(
+      test, {seller: 'https://example.org', coordinatorOrigin: 'example'},
+      'invalid coordinator 1');
+  await testInvalidConfig(
+      test, {seller: 'https://example.org', coordinatorOrigin: 'example.org'},
+      'invalid coordinator 2');
+
+  await testInvalidConfig(
+      test, {seller: 'https://example.org', perBuyerConfig: {'a': {}}},
+      'invalid buyer 1');
+
+  await testInvalidConfig(
+      test,
+      {seller: 'https://example.org', perBuyerConfig: {'http://a.com': {}}},
+      'invalid buyer 2');
+
+  await testInvalidConfig(
+      test, {
+        seller: 'https://example.org',
+        perBuyerConfig: {'https://a.com': {}, 'http://b.com': {}}
+      },
+      'invalid buyer 3');
+
+  await testInvalidConfig(
+      test, {
+        seller: 'https://example.org',
+        perBuyerConfig: {'https://a.com': {}, 'https://b.com': {}}
+      },
+      'missing size info w/per-buyer config 1');
+
+  await testInvalidConfig(
+      test, {
+        seller: 'https://example.org',
+        perBuyerConfig:
+            {'https://a.com': {targetSize: 400}, 'https://b.com': {}}
+      },
+      'missing size info w/per-buyer config 2');
+
+  // These two actually succeed.
+  let result = await navigator.getInterestGroupAdAuctionData({
+    seller: 'https://example.org',
+    perBuyerConfig:
+        {'https://a.com': {targetSize: 400}, 'https://b.com': {targetSize: 500}}
+  });
+  assert_true(result.requestId !== null);
+
+  result = await navigator.getInterestGroupAdAuctionData({
+    seller: 'https://example.org',
+    perBuyerConfig: {'https://a.com': {targetSize: 400}, 'https://b.com': {}},
+    requestSize: 5000
+  });
+  assert_true(result.requestId !== null);
+}, 'getInterestGroupAdAuctionData() config checks');
+
 subsetTest(promise_test, async test => {
   const uuid = generateUuid(test);
   await joinInterestGroup(test, uuid);
@@ -47,6 +113,8 @@
   assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME);
   assert_array_equals(ig.ads, []);
   assert_equals(ig.browserSignals.joinCount, 1, 'joinCount');
+  assert_equals(ig.browserSignals.bidCount, 0, 'bidCount');
+  assert_array_equals(ig.browserSignals.prevWins, []);
 }, 'getInterestGroupAdAuctionData() with one interest group returns a valid result.');
 
 subsetTest(promise_test, async test => {
@@ -70,6 +138,8 @@
   assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME);
   assert_array_equals(ig.ads, ['a', 'b']);
   assert_equals(ig.browserSignals.joinCount, 1, 'joinCount');
+  assert_equals(ig.browserSignals.bidCount, 0, 'bidCount');
+  assert_array_equals(ig.browserSignals.prevWins, []);
 }, 'getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds.');
 
 subsetTest(promise_test, async test => {
@@ -124,3 +194,277 @@
   assert_equals(ig.ads[1].adRenderId, adsArray[1].adRenderId, 'adRenderId 1');
   assert_equals(ig.browserSignals.joinCount, 1, 'joinCount');
 }, 'getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds and include-full-ads.');
+
+// Returns an AuctionAdInterestGroup that sets all fields that can be exported
+// via getInterestGroupAdAuctionData().
+function makeTemplateIgConfig(uuid) {
+  const adsArray = [
+    {
+      renderURL: createRenderURL(uuid) + '&a',
+      adRenderId: 'a',
+      metadata: 'ada',
+      sizeGroup: 'small'
+    },
+    {
+      renderURL: createRenderURL(uuid) + '&b',
+      adRenderId: 'b',
+      metadata: 'adb',
+      sizeGroup: 'big'
+    }
+  ];
+  const adComponentsArray = [
+    {
+      renderURL: 'https://example.org/ca',
+      adRenderId: 'ca',
+      metadata: 'compa',
+      sizeGroup: 'big'
+    },
+    {
+      renderURL: 'https://example.org/cb',
+      adRenderId: 'cb',
+      metadata: 'compb',
+      sizeGroup: 'small'
+    },
+    {
+      renderURL: 'https://example.org/cc',
+      adRenderId: 'cc',
+      metadata: 'compc',
+      sizeGroup: 'big'
+    },
+  ];
+  return {
+    ads: adsArray,
+    adComponents: adComponentsArray,
+    adSizes: {
+      's': {width: '100px', height: '30px'},
+      'xl': {width: '1000px', height: '300px'}
+    },
+    sizeGroups: {'small': ['s'], 'big': ['xl']},
+    trustedBiddingSignalsKeys: ['alpha', 'beta'],
+    userBiddingSignals: 14
+  };
+}
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+  const igConfig = makeTemplateIgConfig(uuid);
+  igConfig.auctionServerRequestFlags = ['include-full-ads'];
+  await joinInterestGroup(test, uuid, igConfig);
+
+  const result = await navigator.getInterestGroupAdAuctionData(
+      {seller: window.location.origin});
+  assert_true(result.requestId !== null);
+  assert_true(result.request.length > 0);
+
+  let decoded = await BA.decodeInterestGroupData(result.request);
+  let ig = validateWithOneIg(decoded);
+
+  assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME);
+  assert_true(ig.ads instanceof Array);
+  assert_equals(ig.ads.length, 2, '# of ads');
+  assert_equals(ig.ads[0].renderURL, igConfig.ads[0].renderURL, 'renderURL 0');
+  assert_equals(ig.ads[1].renderURL, igConfig.ads[1].renderURL, 'renderURL 1');
+  assert_equals(ig.ads[0].adRenderId, 'a', 'adRenderId 0');
+  assert_equals(ig.ads[1].adRenderId, 'b', 'adRenderId 1');
+  assert_equals(ig.ads[0].metadata, '"ada"', 'metadata 0');
+  assert_equals(ig.ads[1].metadata, '"adb"', 'metadata 1');
+  assert_equals(ig.ads[0].sizeGroup, 'small', 'sizegroup 0');
+  assert_equals(ig.ads[1].sizeGroup, 'big', 'sizegroup 1');
+
+  assert_true(ig.components instanceof Array);
+  assert_equals(ig.components.length, 3, '# of component ads');
+  assert_equals(
+      ig.components[0].renderURL, igConfig.adComponents[0].renderURL,
+      'component renderURL 0');
+  assert_equals(
+      ig.components[1].renderURL, igConfig.adComponents[1].renderURL,
+      'component renderURL 1');
+  assert_equals(
+      ig.components[2].renderURL, igConfig.adComponents[2].renderURL,
+      'component renderURL 2');
+  assert_equals(ig.components[0].adRenderId, 'ca', 'component adRenderId 0');
+  assert_equals(ig.components[1].adRenderId, 'cb', 'component adRenderId 1');
+  assert_equals(ig.components[2].adRenderId, 'cc', 'component adRenderId 2');
+  assert_equals(ig.components[0].metadata, '"compa"', 'component metadata 0');
+  assert_equals(ig.components[1].metadata, '"compb"', 'component metadata 1');
+  assert_equals(ig.components[2].metadata, '"compc"', 'component metadata 2');
+  assert_equals(ig.components[0].sizeGroup, 'big', 'component sizegroup 0');
+  assert_equals(ig.components[1].sizeGroup, 'small', 'component sizegroup 1');
+  assert_equals(ig.components[2].sizeGroup, 'big', 'component sizegroup 2');
+
+  assert_true(ig.biddingSignalsKeys instanceof Array);
+  assert_array_equals(ig.biddingSignalsKeys, ['alpha', 'beta']);
+  assert_equals(ig.userBiddingSignals, '14');
+}, 'getInterestGroupAdAuctionData() all IG data fields, with include-full-ads');
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+  const igConfig = makeTemplateIgConfig(uuid);
+  await joinInterestGroup(test, uuid, igConfig);
+
+  const result = await navigator.getInterestGroupAdAuctionData(
+      {seller: window.location.origin});
+  assert_true(result.requestId !== null);
+  assert_true(result.request.length > 0);
+
+  let decoded = await BA.decodeInterestGroupData(result.request);
+  let ig = validateWithOneIg(decoded);
+
+  assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME);
+  assert_array_equals(ig.ads, ['a', 'b']);
+
+  assert_true(ig.components instanceof Array);
+  assert_array_equals(ig.ads, ['a', 'b']);
+  assert_array_equals(ig.components, ['ca', 'cb', 'cc']);
+
+  assert_array_equals(ig.biddingSignalsKeys, ['alpha', 'beta']);
+  assert_equals(ig.userBiddingSignals, '14');
+}, 'getInterestGroupAdAuctionData() all IG data fields, w/o include-full-ads');
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+
+  const igConfig = makeTemplateIgConfig(uuid);
+  igConfig.auctionServerRequestFlags = ['omit-user-bidding-signals'];
+  await joinInterestGroup(test, uuid, igConfig);
+
+  const result = await navigator.getInterestGroupAdAuctionData(
+      {seller: window.location.origin});
+  assert_true(result.requestId !== null);
+  assert_true(result.request.length > 0);
+
+  let decoded = await BA.decodeInterestGroupData(result.request);
+  let ig = validateWithOneIg(decoded);
+
+  assert_equals(ig.name, DEFAULT_INTEREST_GROUP_NAME);
+  assert_array_equals(ig.ads, ['a', 'b']);
+
+  assert_true(ig.components instanceof Array);
+  assert_array_equals(ig.ads, ['a', 'b']);
+  assert_array_equals(ig.components, ['ca', 'cb', 'cc']);
+
+  assert_array_equals(ig.biddingSignalsKeys, ['alpha', 'beta']);
+  assert_false('userBiddingSignals' in ig, 'userBiddingSignals');
+}, 'getInterestGroupAdAuctionData() all IG data fields, with omit-user-bidding-signals');
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+
+  const igConfig = makeTemplateIgConfig(uuid);
+
+  // Join twice.
+  await joinInterestGroup(test, uuid, igConfig);
+  await joinInterestGroup(test, uuid, igConfig);
+
+  // And run an auction. This is a local auction, not a B&A one, run to update
+  // bid/win stats.
+  await runBasicFledgeAuctionAndNavigate(test, uuid);
+  await waitForObservedRequests(
+      uuid, [createBidderReportURL(uuid), createSellerReportURL(uuid)]);
+
+  const result = await navigator.getInterestGroupAdAuctionData(
+      {seller: window.location.origin});
+  assert_true(result.requestId !== null);
+  assert_true(result.request.length > 0);
+
+  let decoded = await BA.decodeInterestGroupData(result.request);
+  let ig = validateWithOneIg(decoded);
+  assert_equals(ig.browserSignals.joinCount, 2, 'joinCount');
+  assert_equals(ig.browserSignals.bidCount, 1, 'bidCount');
+
+  // Recency is the # of seconds since the join. We can't exactly say what it
+  // is, but it shouldn't be too huge.
+  assert_true(typeof ig.browserSignals.recency === 'number');
+  assert_between_inclusive(
+      ig.browserSignals.recency, 0, 60, 'Recency is between 0 and 60 seconds');
+  // It's also supposed to be an integer.
+  assert_equals(
+      ig.browserSignals.recency, Math.round(ig.browserSignals.recency),
+      'Recency is an integer');
+
+  // One win. The format here depends highly on whether full ads are used or
+  // not.
+  assert_true(
+      ig.browserSignals.prevWins instanceof Array, 'prevWins is an array');
+  assert_equals(ig.browserSignals.prevWins.length, 1, 'prevWins length');
+  assert_true(
+      ig.browserSignals.prevWins[0] instanceof Array,
+      'prevWins[0] is an array');
+  assert_equals(ig.browserSignals.prevWins[0].length, 2, 'prevWins[0] length');
+
+  // prevWins[0][0] is the time delta in second again.
+  let prevWinTime = ig.browserSignals.prevWins[0][0];
+  assert_true(typeof prevWinTime === 'number');
+  assert_between_inclusive(
+      prevWinTime, 0, 60, 'prevWinTime is between 0 and 60 seconds');
+  // It's also supposed to be an integer.
+  assert_equals(
+      prevWinTime, Math.round(prevWinTime), 'prevWinTime is an integer');
+
+  // prevWins[0][1] is the adRenderId of the winner.
+  assert_equals(ig.browserSignals.prevWins[0][1], 'a');
+}, 'getInterestGroupAdAuctionData() browserSignals');
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+
+  const igConfig = makeTemplateIgConfig(uuid);
+  igConfig.auctionServerRequestFlags = ['include-full-ads'];
+
+  // Join twice.
+  await joinInterestGroup(test, uuid, igConfig);
+  await joinInterestGroup(test, uuid, igConfig);
+
+  // And run an auction. This is a local auction, not a B&A one, run to update
+  // bid/win stats.
+  await runBasicFledgeAuctionAndNavigate(test, uuid);
+  await waitForObservedRequests(
+      uuid, [createBidderReportURL(uuid), createSellerReportURL(uuid)]);
+
+  const result = await navigator.getInterestGroupAdAuctionData(
+      {seller: window.location.origin});
+  assert_true(result.requestId !== null);
+  assert_true(result.request.length > 0);
+
+  let decoded = await BA.decodeInterestGroupData(result.request);
+  let ig = validateWithOneIg(decoded);
+  assert_equals(ig.browserSignals.joinCount, 2, 'joinCount');
+  assert_equals(ig.browserSignals.bidCount, 1, 'bidCount');
+
+  // Recency is the # of seconds since the join. We can't exactly say what it
+  // is, but it shouldn't be too huge.
+  assert_true(typeof ig.browserSignals.recency === 'number');
+  assert_between_inclusive(
+      ig.browserSignals.recency, 0, 60, 'Recency is between 0 and 60 seconds');
+  // It's also supposed to be an integer.
+  assert_equals(
+      ig.browserSignals.recency, Math.round(ig.browserSignals.recency),
+      'Recency is an integer');
+
+  // One win. The format here depends highly on whether full ads are used or
+  // not.
+  assert_true(
+      ig.browserSignals.prevWins instanceof Array, 'prevWins is an array');
+  assert_equals(ig.browserSignals.prevWins.length, 1, 'prevWins length');
+  assert_true(
+      ig.browserSignals.prevWins[0] instanceof Array,
+      'prevWins[0] is an array');
+  assert_equals(ig.browserSignals.prevWins[0].length, 2, 'prevWins[0] length');
+
+  // prevWins[0][0] is the time delta in second again.
+  let prevWinTime = ig.browserSignals.prevWins[0][0];
+  assert_true(typeof prevWinTime === 'number');
+  assert_between_inclusive(
+      prevWinTime, 0, 60, 'prevWinTime is between 0 and 60 seconds');
+  // It's also supposed to be an integer.
+  assert_equals(
+      prevWinTime, Math.round(prevWinTime), 'prevWinTime is an integer');
+
+  // prevWins[0][1] is an ad object w/include-full-ads on (with renderURL,
+  // metadata, and adRenderId).
+  let prevWinAd = ig.browserSignals.prevWins[0][1];
+  assert_equals(
+      prevWinAd.renderURL, igConfig.ads[0].renderURL, 'prevWin ad renderURL');
+  assert_equals(prevWinAd.metadata, '"ada"', 'prevWin ad metadata');
+  assert_equals(prevWinAd.adRenderId, 'a', 'prevWin ad adRenderId');
+}, 'getInterestGroupAdAuctionData() browserSignals with include-full-ads');
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_1-4-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_1-4-expected.txt
index a4c2fd64..aa9f1b29 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_1-4-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_1-4-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 [FAIL] getInterestGroupAdAuctionData() with no interest groups returns a zero length result.
   promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
+[FAIL] getInterestGroupAdAuctionData() config checks
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
 [FAIL] getInterestGroupAdAuctionData() with one interest group returns a valid result.
   promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
 [FAIL] getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds.
   promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
-[FAIL] getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds and omit-ads.
-  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_5-8-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_5-8-expected.txt
new file mode 100644
index 0000000..9c4ee81
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_5-8-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+[FAIL] getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds and omit-ads.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
+[FAIL] getInterestGroupAdAuctionData() with one interest group with two ads w/renderIds and include-full-ads.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
+[FAIL] getInterestGroupAdAuctionData() all IG data fields, with include-full-ads
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
+[FAIL] getInterestGroupAdAuctionData() all IG data fields, w/o include-full-ads
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_9-12-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_9-12-expected.txt
new file mode 100644
index 0000000..e7099c5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_9-12-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] getInterestGroupAdAuctionData() all IG data fields, with omit-user-bidding-signals
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
+[FAIL] getInterestGroupAdAuctionData() browserSignals
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
+[FAIL] getInterestGroupAdAuctionData() browserSignals with include-full-ads
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.getInterestGroupAdAuctionData is not a function"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js b/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js
new file mode 100644
index 0000000..dd36973
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window.js
@@ -0,0 +1,37 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/partitioned-popins/resources/proxy-helpers.js
+
+'use strict';
+
+// Spec: https://explainers-by-googlers.github.io/partitioned-popins/
+// Step 1 (window) Set up listener to resolve messages as they come in.
+// Step 2 (window) Open cross-site popin.
+// Step 3 (popin) Set up listener to resolve messages as they come in.
+// Step 4 (popin) Test and report usable methods against window.
+// Step 5 (window) Test and compare usable methods against popin.
+// Step 6 (popin) Cleanup.
+// Step 7 (window) Cleanup.
+
+async_test(t => {
+  let popin_proxy;
+
+  // Step 1
+  window.addEventListener("message", t.step_func(e => {
+    switch (e.data.type) {
+      case 'ready':
+        // Step 5
+        assert_equals(e.data.message, "Closed,Then,");
+        assert_equals(getUsableMethods(popin_proxy), "Closed,Then,");
+        popin_proxy.postMessage({type: "cleanup"}, "*");
+        break;
+      case 'cleanup':
+        // Step 7
+        t.done();
+        break;
+    }
+  }));
+
+  // Step 2
+  popin_proxy = window.open("https://{{hosts[alt][]}}:{{ports[https][0]}}/partitioned-popins/resources/partitioned-popins.proxy-popin.html", '_blank', 'popin');
+}, "Verify cross-site Partitioned Popins proxies only have access to postMessage and closed methods.");
diff --git a/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window-expected.txt
new file mode 100644
index 0000000..0410d90
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Verify same-origin Partitioned Popins proxies only have access to postMessage and closed methods.
+  assert_equals: expected "Closed,Then," but got "Closed,Blur,OnBlur,Opener,Length,Name,AnonymousName,CustomMethod,Then,"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js
new file mode 100644
index 0000000..1bda23eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window.js
@@ -0,0 +1,39 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/partitioned-popins/resources/proxy-helpers.js
+
+'use strict';
+
+// Spec: https://explainers-by-googlers.github.io/partitioned-popins/
+// Step 1 (window) Set up listener to resolve messages as they come in.
+// Step 2 (window) Open same-origin popin.
+// Step 3 (popin) Set up listener to resolve messages as they come in.
+// Step 4 (popin) Test and report usable methods against window.
+// Step 5 (window) Test and compare usable methods against popin.
+// Step 6 (popin) Cleanup.
+// Step 7 (window) Cleanup.
+
+// TODO(crbug.com/340606651): Remove expectations file and secure same-origin popins.
+
+async_test(t => {
+  let popin_proxy;
+
+  // Step 1
+  window.addEventListener("message", t.step_func(e => {
+    switch (e.data.type) {
+      case 'ready':
+        // Step 5
+        assert_equals(e.data.message, "Closed,Then,");
+        assert_equals(getUsableMethods(popin_proxy), "Closed,Then,");
+        popin_proxy.postMessage({type: "cleanup"}, "*");
+        break;
+      case 'cleanup':
+        // Step 7
+        t.done();
+        break;
+    }
+  }));
+
+  // Step 2
+  popin_proxy = window.open("/partitioned-popins/resources/partitioned-popins.proxy-popin.html", '_blank', 'popin');
+}, "Verify same-origin Partitioned Popins proxies only have access to postMessage and closed methods.");
diff --git a/third_party/blink/web_tests/external/wpt/partitioned-popins/resources/partitioned-popins.proxy-popin.html b/third_party/blink/web_tests/external/wpt/partitioned-popins/resources/partitioned-popins.proxy-popin.html
new file mode 100644
index 0000000..d1b7b86
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/partitioned-popins/resources/partitioned-popins.proxy-popin.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/partitioned-popins/resources/proxy-helpers.js"></script>
+<script>
+(async function() {
+  test_driver.set_test_context(window.opener);
+
+  // Step 3 (partitioned-popins/partitioned-popins.proxy-{}.tentative.sub.https.window.js)
+  window.addEventListener("message", e => {
+    switch (e.data.type) {
+      case 'cleanup':
+        // Step 6 (partitioned-popins/partitioned-popins.proxy-{}.tentative.sub.https.window.js)
+        window.opener.postMessage({type: "cleanup"}, "*");
+        window.close();
+        break;
+    }
+  });
+
+  // Step 4 (partitioned-popins/partitioned-popins.proxy-{}.tentative.sub.https.window.js)
+  window.opener.postMessage({type: "ready", message: getUsableMethods(window.opener)}, "*");
+})();
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/partitioned-popins/resources/proxy-helpers.js b/third_party/blink/web_tests/external/wpt/partitioned-popins/resources/proxy-helpers.js
new file mode 100644
index 0000000..46359df
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/partitioned-popins/resources/proxy-helpers.js
@@ -0,0 +1,58 @@
+'use strict';
+
+function customMethod() {
+}
+
+let customAttribute = "";
+
+function getUsableMethods(proxy) {
+  let message = "";
+  try {
+    proxy.closed;
+    message += "Closed,"
+  } catch (_) {}
+  try {
+    proxy.blur();
+    message += "Blur,"
+  } catch (_) {}
+  try {
+    proxy.onblur;
+    message += "OnBlur,"
+  } catch (_) {}
+  try {
+    proxy.opener;
+    message += "Opener,"
+  } catch (_) {}
+  try {
+    proxy.length;
+    message += "Length,"
+  } catch (_) {}
+  try {
+    proxy.name = "foo";
+    message += "Name,"
+  } catch (_) {}
+  try {
+    proxy[0];
+    message += "AnonymousIndex,"
+  } catch (_) {}
+  try {
+    proxy['test'];
+    message += "AnonymousName,"
+  } catch (_) {}
+  try {
+    proxy.customMethod();
+    message += "CustomMethod,"
+  } catch (_) {}
+  try {
+    proxy.customAttribute;
+    message += "CustomAttributeGet,"
+  } catch (_) {}
+  try {
+    proxy.customAttribute = "";
+    message += "CustomAttributeSet,"
+  } catch (_) {}
+  if (proxy.then == undefined) {
+    message += "Then,"
+  }
+  return message;
+}
diff --git a/third_party/blink/web_tests/external/wpt/pointerlock/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/pointerlock/idlharness.window-expected.txt
deleted file mode 100644
index 638dac0..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerlock/idlharness.window-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Element interface: operation requestPointerLock(optional PointerLockOptions)
-  assert_unreached: Throws "TypeError: Illegal invocation" instead of rejecting promise Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/js-string/constants.tentative.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/js-string/constants.tentative.any-expected.txt
deleted file mode 100644
index b5a93e3..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/js-string/constants.tentative.any-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] constants
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 1
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 2
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 3
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 4
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 5
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 6
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 7
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 8
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 9
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/js-string/constants.tentative.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/js-string/constants.tentative.any.worker-expected.txt
deleted file mode 100644
index b5a93e3..0000000
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/js-string/constants.tentative.any.worker-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] constants
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 1
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 2
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 3
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 4
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 5
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 6
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 7
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 8
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-[FAIL] constants 9
-  assert_throws_js: type mismatch function "() => instantiateImportedGlobal("'", "constant", type, mutable, "'")" threw object "LinkError: WebAssembly.Module(): String constant import #0 "constant" must be an immutable global subtyping externref @+11" ("LinkError") expected instance of function "function CompileError() { [native code] }" ("CompileError")
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse.py
index 511bda66..7077d7bb 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse.py
@@ -54,26 +54,34 @@
     assert expected == filtered_events[1:]
 
 
-@pytest.mark.parametrize("origin", ["element", "pointer", "viewport"])
-async def test_params_actions_origin_outside_viewport(
-    bidi_session, top_context, get_actions_origin_page, get_element, origin
-):
-    if origin == "element":
-        url = get_actions_origin_page(
-            """width: 100px; height: 50px; background: green;
-            position: relative; left: -200px; top: -100px;"""
-        )
-        await bidi_session.browsing_context.navigate(
-            context=top_context["context"],
-            url=url,
-            wait="complete",
+@pytest.mark.parametrize("origin", ["pointer", "viewport"])
+async def test_params_actions_origin_outside_viewport(bidi_session, top_context, origin):
+    actions = Actions()
+    actions.add_pointer().pointer_move(x=-50, y=-50, origin=origin)
+
+    with pytest.raises(MoveTargetOutOfBoundsException):
+        await bidi_session.input.perform_actions(
+            actions=actions, context=top_context["context"]
         )
 
-        element = await get_element("#inner")
-        origin = get_element_origin(element)
+
+async def test_params_actions_origin_element_outside_viewport(
+    bidi_session, top_context, get_actions_origin_page, get_element
+):
+    url = get_actions_origin_page(
+        """width: 100px; height: 50px; background: green;
+           position: relative; left: -200px; top: -100px;"""
+    )
+    await bidi_session.browsing_context.navigate(
+        context=top_context["context"],
+        url=url,
+        wait="complete",
+    )
+
+    elem = await get_element("#inner")
 
     actions = Actions()
-    actions.add_pointer().pointer_move(x=-100, y=-100, origin=origin)
+    actions.add_pointer().pointer_move(x=0, y=0, origin=get_element_origin(elem))
 
     with pytest.raises(MoveTargetOutOfBoundsException):
         await bidi_session.input.perform_actions(
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_pen.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_pen.py
index 44ac435a..7b68d3d 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_pen.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_pen.py
@@ -1,6 +1,5 @@
 import pytest
 
-from webdriver.bidi.error import MoveTargetOutOfBoundsException
 from webdriver.bidi.modules.input import Actions, get_element_origin
 
 from .. import get_events
@@ -14,36 +13,6 @@
 pytestmark = pytest.mark.asyncio
 
 
-@pytest.mark.parametrize("origin", ["element", "pointer", "viewport"])
-async def test_params_actions_origin_outside_viewport(
-    bidi_session, get_actions_origin_page, top_context, get_element, origin
-):
-    if origin == "element":
-        url = get_actions_origin_page(
-            """width: 100px; height: 50px; background: green;
-            position: relative; left: -200px; top: -100px;"""
-        )
-        await bidi_session.browsing_context.navigate(
-            context=top_context["context"],
-            url=url,
-            wait="complete",
-        )
-
-        element = await get_element("#inner")
-        origin = get_element_origin(element)
-
-    actions = Actions()
-    (
-        actions.add_pointer(pointer_type="pen")
-        .pointer_move(x=-100, y=-100, origin=origin)
-    )
-
-    with pytest.raises(MoveTargetOutOfBoundsException):
-        await bidi_session.input.perform_actions(
-            actions=actions, context=top_context["context"]
-        )
-
-
 @pytest.mark.parametrize("mode", ["open", "closed"])
 @pytest.mark.parametrize("nested", [False, True], ids=["outer", "inner"])
 async def test_pen_pointer_in_shadow_tree(
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_touch.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_touch.py
index 6edc7f8..f036de7c 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_touch.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_touch.py
@@ -1,6 +1,5 @@
 import pytest
 
-from webdriver.bidi.error import MoveTargetOutOfBoundsException
 from webdriver.bidi.modules.input import Actions, get_element_origin
 
 from .. import get_events
@@ -14,36 +13,6 @@
 pytestmark = pytest.mark.asyncio
 
 
-@pytest.mark.parametrize("origin", ["element", "pointer", "viewport"])
-async def test_params_actions_origin_outside_viewport(
-    bidi_session, get_actions_origin_page, top_context, get_element, origin
-):
-    if origin == "element":
-        url = get_actions_origin_page(
-            """width: 100px; height: 50px; background: green;
-            position: relative; left: -200px; top: -100px;"""
-        )
-        await bidi_session.browsing_context.navigate(
-            context=top_context["context"],
-            url=url,
-            wait="complete",
-        )
-
-        element = await get_element("#inner")
-        origin = get_element_origin(element)
-
-    actions = Actions()
-    (
-        actions.add_pointer(pointer_type="touch")
-        .pointer_move(x=-100, y=-100, origin=origin)
-    )
-
-    with pytest.raises(MoveTargetOutOfBoundsException):
-        await bidi_session.input.perform_actions(
-            actions=actions, context=top_context["context"]
-        )
-
-
 @pytest.mark.parametrize("mode", ["open", "closed"])
 @pytest.mark.parametrize("nested", [False, True], ids=["outer", "inner"])
 async def test_touch_pointer_in_shadow_tree(
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/wheel.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/wheel.py
index 0a1af02..3129e9b0 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/wheel.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/wheel.py
@@ -1,6 +1,6 @@
 import pytest
 
-from webdriver.bidi.error import MoveTargetOutOfBoundsException, NoSuchFrameException
+from webdriver.bidi.error import NoSuchFrameException
 from webdriver.bidi.modules.input import Actions, get_element_origin
 from webdriver.bidi.modules.script import ContextTarget
 
@@ -20,23 +20,6 @@
         await bidi_session.input.perform_actions(actions=actions, context="foo")
 
 
-@pytest.mark.parametrize("origin", ["element", "viewport"])
-async def test_params_actions_origin_outside_viewport(
-    bidi_session, setup_wheel_test, top_context, get_element, origin
-):
-    if origin == "element":
-        element = await get_element("#scrollable")
-        origin = get_element_origin(element)
-
-    actions = Actions()
-    actions.add_wheel().scroll(x=-100, y=-100, delta_x=10, delta_y=20, origin=origin)
-
-    with pytest.raises(MoveTargetOutOfBoundsException):
-        await bidi_session.input.perform_actions(
-            actions=actions, context=top_context["context"]
-        )
-
-
 @pytest.mark.parametrize("delta_x, delta_y", [(0, 10), (5, 0), (5, 10)])
 async def test_scroll_not_scrollable(
     bidi_session, setup_wheel_test, top_context, get_element, delta_x, delta_y
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py
index 53ec6d2..f8683ce 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py
@@ -1,11 +1,6 @@
 import pytest
 
-from webdriver.error import (
-    InvalidArgumentException,
-    MoveTargetOutOfBoundsException,
-    NoSuchWindowException,
-    StaleElementReferenceException,
-)
+from webdriver.error import InvalidArgumentException, NoSuchWindowException, StaleElementReferenceException
 
 from tests.classic.perform_actions.support.mouse import (
     get_inview_center,
@@ -42,15 +37,6 @@
         mouse_chain.click(element=element).perform()
 
 
-@pytest.mark.parametrize("origin", ["element", "pointer", "viewport"])
-def test_params_actions_origin_outside_viewport(session, test_actions_page, mouse_chain, origin):
-    if origin == "element":
-        origin = session.find.css("#outer", all=False)
-
-    with pytest.raises(MoveTargetOutOfBoundsException):
-        mouse_chain.pointer_move(-100, -100, origin=origin).perform()
-
-
 def test_click_at_coordinates(session, test_actions_page, mouse_chain):
     div_point = {
         "x": 82,
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_pen.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_pen.py
index 7c384ca9..bf71a20 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_pen.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_pen.py
@@ -1,10 +1,6 @@
 import pytest
 
-from webdriver.error import (
-    MoveTargetOutOfBoundsException,
-    NoSuchWindowException,
-    StaleElementReferenceException,
-)
+from webdriver.error import NoSuchWindowException, StaleElementReferenceException
 
 from tests.classic.perform_actions.support.mouse import (
     get_inview_center,
@@ -38,15 +34,6 @@
         pen_chain.click(element=element).perform()
 
 
-@pytest.mark.parametrize("origin", ["element", "pointer", "viewport"])
-def test_params_actions_origin_outside_viewport(session, test_actions_page, pen_chain, origin):
-    if origin == "element":
-        origin = session.find.css("#outer", all=False)
-
-    with pytest.raises(MoveTargetOutOfBoundsException):
-        pen_chain.pointer_move(-100, -100, origin=origin).perform()
-
-
 @pytest.mark.parametrize("mode", ["open", "closed"])
 @pytest.mark.parametrize("nested", [False, True], ids=["outer", "inner"])
 def test_pen_pointer_in_shadow_tree(
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_touch.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_touch.py
index 70ffd68..b85b2e6e 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_touch.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_touch.py
@@ -1,10 +1,6 @@
 import pytest
 
-from webdriver.error import (
-    MoveTargetOutOfBoundsException,
-    NoSuchWindowException,
-    StaleElementReferenceException
-)
+from webdriver.error import NoSuchWindowException, StaleElementReferenceException
 from tests.classic.perform_actions.support.mouse import (
     get_inview_center,
     get_viewport_rect,
@@ -13,7 +9,6 @@
 
 from . import assert_pointer_events, record_pointer_events
 
-
 def test_null_response_value(session, touch_chain):
     value = touch_chain.click().perform()
     assert value is None
@@ -37,15 +32,6 @@
         touch_chain.click(element=element).perform()
 
 
-@pytest.mark.parametrize("origin", ["element", "pointer", "viewport"])
-def test_params_actions_origin_outside_viewport(session, test_actions_page, touch_chain, origin):
-    if origin == "element":
-        origin = session.find.css("#outer", all=False)
-
-    with pytest.raises(MoveTargetOutOfBoundsException):
-        touch_chain.pointer_move(-100, -100, origin=origin).perform()
-
-
 @pytest.mark.parametrize("mode", ["open", "closed"])
 @pytest.mark.parametrize("nested", [False, True], ids=["outer", "inner"])
 def test_touch_pointer_in_shadow_tree(
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/wheel.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/wheel.py
index 0840327..1c9bf08 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/wheel.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/wheel.py
@@ -1,6 +1,6 @@
 import pytest
 
-from webdriver.error import MoveTargetOutOfBoundsException, NoSuchWindowException
+from webdriver.error import NoSuchWindowException
 
 import time
 from tests.classic.perform_actions.support.refine import get_events
@@ -23,17 +23,6 @@
         wheel_chain.scroll(0, 0, 0, 10).perform()
 
 
-@pytest.mark.parametrize("origin", ["element", "viewport"])
-def test_params_actions_origin_outside_viewport(
-    session, test_actions_scroll_page, wheel_chain, origin
-):
-    if origin == "element":
-        origin = session.find.css("#scrollable", all=False)
-
-    with pytest.raises(MoveTargetOutOfBoundsException):
-        wheel_chain.scroll(-100, -100, 10, 20, origin="viewport").perform()
-
-
 def test_scroll_not_scrollable(session, test_actions_scroll_page, wheel_chain):
     target = session.find.css("#not-scrollable", all=False)
 
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/matmul.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/matmul.https.any.js
index 58410a6..888e511 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/matmul.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/matmul.https.any.js
@@ -425,7 +425,8 @@
     }
   },
   {
-    'name': 'matmul float32 5D and 5D tensors',
+    'name':
+        'matmul float32 5D and 5D tensors, broadcast the two leftmost dimensions of inputB',
     'graph': {
       'inputs': {
         'inputA': {
@@ -497,6 +498,86 @@
     }
   },
   {
+    'name':
+        'matmul float32 5D and 5D tensors, broadcast the leftmost dimensions of inputB',
+    'graph': {
+      'inputs': {
+        'inputA': {
+          'data': [
+            33.75957107543945,  97.24552917480469,  83.7085189819336,
+            64.53984069824219,  29.57938003540039,  17.19923973083496,
+            67.94749450683594,  97.45838165283203,  54.449283599853516,
+            29.552200317382812, 51.99970245361328,  36.03101348876953,
+            9.701058387756348,  27.04842185974121,  6.020919322967529,
+            22.940902709960938, 53.1243896484375,   15.292234420776367,
+            48.21302795410156,  87.40799713134766,  51.34442138671875,
+            21.1557559967041,   27.589487075805664, 58.412384033203125,
+            5.963276386260986,  84.74938201904297,  55.45738220214844,
+            50.858699798583984, 23.763574600219727, 62.330928802490234,
+            35.774959564208984, 17.340242385864258, 29.16901397705078,
+            23.191360473632812, 27.060928344726562, 1.2828527688980103,
+            8.720425605773926,  48.45281219482422,  99.0130386352539,
+            65.86412048339844,  92.69683074951172,  85.43540954589844,
+            37.49127960205078,  51.397132873535156, 53.19015121459961,
+            38.33119201660156,  75.20586395263672,  3.8537938594818115
+          ],
+          'descriptor': {'dimensions': [2, 2, 1, 3, 4], 'dataType': 'float32'}
+        },
+        'inputB': {
+          'data': [
+            88.1700439453125,   78.4012680053711,   14.819003105163574,
+            3.6923038959503174, 45.906429290771484, 43.083919525146484,
+            47.199466705322266, 60.92521667480469,  8.162760734558105,
+            20.333263397216797, 20.438398361206055, 27.0194091796875,
+            15.601424217224121, 87.46969604492188,  65.79554748535156,
+            69.31697082519531,  31.984439849853516, 12.291812896728516,
+            13.304834365844727, 85.26705169677734,  88.1700439453125,
+            78.4012680053711,   14.819003105163574, 3.6923038959503174,
+            45.906429290771484, 43.083919525146484, 47.199466705322266,
+            60.92521667480469,  8.162760734558105,  20.333263397216797,
+            20.438398361206055, 27.0194091796875,   15.601424217224121,
+            87.46969604492188,  65.79554748535156,  69.31697082519531,
+            31.984439849853516, 12.291812896728516, 13.304834365844727,
+            85.26705169677734
+          ],
+          'descriptor': {'dimensions': [1, 2, 1, 4, 5], 'dataType': 'float32'}
+        }
+      },
+      'operators': [{
+        'name': 'matmul',
+        'arguments': [{'a': 'inputA'}, {'b': 'inputB'}],
+        'outputs': 'output'
+      }],
+      'expectedOutputs': {
+        'output': {
+          'data': [
+            13350.8759765625,  11562.755859375,   8524.271484375,
+            9099.0927734375,   14537.8701171875,  11493.283203125,
+            8083.90869140625,  3744.22216796875,  7489.62353515625,
+            14488.2314453125,  9634.3720703125,   8221.173828125,
+            3861.51416015625,  5470.0556640625,   9594.072265625,
+            3733.946533203125, 2933.679931640625, 2167.611083984375,
+            1088.48193359375,  3347.576416015625, 12387.083984375,
+            8985.1884765625,   3545.52783203125,  5701.10595703125,
+            13374.9169921875,  10051.3671875,     7637.7470703125,
+            3198.221435546875, 3552.6796875,      9583.1220703125,
+            8835.94921875,     7592.7666015625,   6742.10400390625,
+            6241.31396484375,  9982.404296875,    6713.85205078125,
+            6326.3173828125,   4920.9609375,      3956.46875,
+            6190.67626953125,  4213.013671875,    4153.708984375,
+            2283.152099609375, 2681.085693359375, 3700.47509765625,
+            9445.5869140625,   7752.5400390625,   5435.56005859375,
+            9964.6591796875,   13516.18359375,    16182.931640625,
+            13956.9560546875,  7795.52685546875,  5002.8349609375,
+            12841.802734375,   8145.45654296875,  8134.66650390625,
+            4344.25,           7138.79052734375,  8497.98046875
+          ],
+          'descriptor': {'dimensions': [2, 2, 1, 3, 5], 'dataType': 'float32'}
+        }
+      }
+    }
+  },
+  {
     'name': 'matmul float32 5D and 2D tensors',
     'graph': {
       'inputs': {
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index 78dfac2..050f9ea 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -244,6 +244,7 @@
 mask-repeat: repeat
 mask-size: auto
 mask-type: luminance
+masonry-slack: normal
 masonry-template-tracks: auto
 masonry-track-end: auto
 masonry-track-start: auto
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index 0af8e1b..b84ca843 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -244,6 +244,7 @@
 mask-repeat: repeat
 mask-size: auto
 mask-type: luminance
+masonry-slack: normal
 masonry-template-tracks: auto
 masonry-track-end: auto
 masonry-track-start: auto
diff --git a/third_party/blink/web_tests/fast/dom/HTMLMeterElement/meter-styles.html b/third_party/blink/web_tests/fast/dom/HTMLMeterElement/meter-styles.html
index ac8d668..35a5a70 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLMeterElement/meter-styles.html
+++ b/third_party/blink/web_tests/fast/dom/HTMLMeterElement/meter-styles.html
@@ -28,7 +28,7 @@
       <li><meter style="border-color: #224; padding: 5px 20px 5px 10px;" min="0" max="100" low="30" high="60" optimum="100" value="80" ></meter> has padding</li>
       <li><meter style="border-color: #224; margin:  5px 20px 5px 10px;" min="0" max="100" low="30" high="60" optimum="100" value="80" ></meter> has margin</li>
       <li><meter style="box-shadow: 4px 4px 10px rgba(255,0,0,0.5), inset 4px 4px 4px rgba(0,255,0,0.5);"></meter> has box-shadow</li>
-      <li><meter style="background: blue; color: white;" value="50">50</meter> Background CSS property does not disable -webkit-appearance.</li>
+      <li><meter style="background: blue; color: white;" value="50">50</meter> has background</li>
     </ul>
   </div>
   <h2>Providing bar and/or value styles</h2>
diff --git a/third_party/blink/web_tests/fast/events/event-input-contentEditable-expected.txt b/third_party/blink/web_tests/fast/events/event-input-contentEditable-expected.txt
index 5bcb4e59..cabca531 100644
--- a/third_party/blink/web_tests/fast/events/event-input-contentEditable-expected.txt
+++ b/third_party/blink/web_tests/fast/events/event-input-contentEditable-expected.txt
@@ -7,7 +7,7 @@
 PASS event.target.id is 'target2'
 PASS event.target.innerHTML is 'This text should not be changed.'
 PASS event.target.id is 'target3'
-PASS event.target.innerHTML is ''
+PASS event.target.innerHTML is '<br>'
 PASS event.target.id is 'target4'
 PASS event.target.innerHTML is '<a href="http://www.example.com/">This text should be a link.</a>'
 PASS event.target.id is 'target6parent'
@@ -15,7 +15,7 @@
 PASS event.target.id is 'target7'
 PASS event.target.innerHTML is 'X'
 PASS event.target.id is 'target8'
-PASS event.target.innerHTML is ''
+PASS event.target.innerHTML is '<br>'
 PASS event.target.id is 'target9parent'
 PASS event.target.innerHTML is '<div id="target9child" contenteditable="">Replacing</div>'
 PASS event.target.id is 't10gch'
diff --git a/third_party/blink/web_tests/fast/events/inputevents/inputevent-yank.html b/third_party/blink/web_tests/fast/events/inputevents/inputevent-yank.html
index 0a9fb6f..0a28e51 100644
--- a/third_party/blink/web_tests/fast/events/inputevents/inputevent-yank.html
+++ b/third_party/blink/web_tests/fast/events/inputevents/inputevent-yank.html
@@ -24,7 +24,7 @@
 
     // Delete a word to setup kill buffer.
     eventSender.keyDown('Backspace', ['altKey']);
-    assert_equals(editable.innerHTML, '');
+    assert_equals(editable.innerHTML, '<br>');
 
     // Test Yank.
     eventRecorder = '';
diff --git a/third_party/blink/web_tests/fast/events/key-events-in-editable-flexbox-expected.txt b/third_party/blink/web_tests/fast/events/key-events-in-editable-flexbox-expected.txt
index 92ee564..8ac9b969 100644
--- a/third_party/blink/web_tests/fast/events/key-events-in-editable-flexbox-expected.txt
+++ b/third_party/blink/web_tests/fast/events/key-events-in-editable-flexbox-expected.txt
@@ -3,6 +3,6 @@
 TEST COMPLETE
 
 PASS targetDiv.innerText is "TEST"
-PASS targetDiv.innerText is ""
+PASS targetDiv.innerHTML is "<br>"
 PASS targetDiv.innerText is "TEST"
 TEST
diff --git a/third_party/blink/web_tests/fast/events/key-events-in-editable-flexbox.html b/third_party/blink/web_tests/fast/events/key-events-in-editable-flexbox.html
index 0beb304b..bea74191 100644
--- a/third_party/blink/web_tests/fast/events/key-events-in-editable-flexbox.html
+++ b/third_party/blink/web_tests/fast/events/key-events-in-editable-flexbox.html
@@ -28,7 +28,7 @@
           eventSender.keyDown("Backspace", []);
           eventSender.keyDown("Backspace", []);
           eventSender.keyDown("Backspace", []);
-          shouldBeEmptyString("targetDiv.innerText");
+          shouldBeEqualToString("targetDiv.innerHTML", "<br>");
 
           eventSender.keyDown('T');
           eventSender.keyDown('E');
diff --git a/third_party/blink/web_tests/fast/events/key-events-in-editable-gridbox-expected.txt b/third_party/blink/web_tests/fast/events/key-events-in-editable-gridbox-expected.txt
index caa3cab..b7d75949 100644
--- a/third_party/blink/web_tests/fast/events/key-events-in-editable-gridbox-expected.txt
+++ b/third_party/blink/web_tests/fast/events/key-events-in-editable-gridbox-expected.txt
@@ -1,11 +1,11 @@
 PASS targetDiv.innerText is "TEST"
-PASS targetDiv.innerText is ""
+PASS targetDiv.innerHTML is "<br>"
 PASS targetDiv.innerText is "TEST"
 PASS targetDiv.innerText is "TEST"
-PASS targetDiv.innerText is ""
+PASS targetDiv.innerHTML is "<br>"
 PASS targetDiv.innerText is "TEST"
 PASS targetDiv.innerText is "TEST"
-PASS targetDiv.innerText is ""
+PASS targetDiv.innerHTML is "<br>"
 PASS targetDiv.innerText is "TEST"
 PASS successfullyParsed is true
 
diff --git a/third_party/blink/web_tests/fast/events/key-events-in-editable-gridbox.html b/third_party/blink/web_tests/fast/events/key-events-in-editable-gridbox.html
index 523a5724..35c05d4c 100644
--- a/third_party/blink/web_tests/fast/events/key-events-in-editable-gridbox.html
+++ b/third_party/blink/web_tests/fast/events/key-events-in-editable-gridbox.html
@@ -26,7 +26,7 @@
     document.execCommand("delete");
     document.execCommand("delete");
     document.execCommand("delete");
-    shouldBeEmptyString("targetDiv.innerText");
+    shouldBeEqualToString("targetDiv.innerHTML", "<br>");
 
     document.execCommand("insertText", false, "TEST");
     shouldBeEqualToString("targetDiv.innerText", "TEST");
diff --git a/third_party/blink/web_tests/fast/events/script-tests/event-input-contentEditable.js b/third_party/blink/web_tests/fast/events/script-tests/event-input-contentEditable.js
index 401c629b..8dc07376 100644
--- a/third_party/blink/web_tests/fast/events/script-tests/event-input-contentEditable.js
+++ b/third_party/blink/web_tests/fast/events/script-tests/event-input-contentEditable.js
@@ -51,11 +51,11 @@
 document.execCommand("insertText", false, target2Text);
 
 // An "delete" command should dispatch an input event.
-var target3 = setupForFiringTest('<p id="target3" contentEditable>This text shouldn be deleted.</p>', '');
+var target3 = setupForFiringTest('<p id="target3" contentEditable>This text shouldn be deleted.</p>', '<br>');
 document.execCommand("delete", false);
 
 // A command other than text-editing should dispatch an input event.
-// Also note that createLink is a composite command, 
+// Also note that createLink is a composite command,
 // so this test also ensures that even composite command dispatches the event only once.
 var target4 = setupForFiringTest('<p id="target4" contentEditable>This text should be a link.</p>', "<a href=\"http://www.example.com/\">This text should be a link.</a>");
 document.execCommand("createLink", false, "http://www.example.com/");
@@ -88,7 +88,7 @@
 sel.selectAllChildren(target7);
 eventSender.keyDown('X');
 
-var target8 = setupForFiringTest('<p id="target8" contentEditable>Deleted</p>', '');
+var target8 = setupForFiringTest('<p id="target8" contentEditable>Deleted</p>', '<br>');
 sel.selectAllChildren(target8);
 eventSender.keyDown('Delete');
 
diff --git a/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl.html b/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl.html
index 11b70ab..2b69ccc 100644
--- a/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl.html
+++ b/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl.html
@@ -31,7 +31,7 @@
   <li>Check that padding is created:</li>
   <li>Check that margin is created:</li>
   <li>Check that box shadow is drawn:</li>
-  <li>Check that background CSS property disables -webkit-appearance:</li>
+  <li>Check that background CSS property disables appearance:</li>
 </ul>
 <ul>
   <meter style="border-color: #000000; border-style: solid; border-width: 5px;" min="0" max="100" low="30" high="60" optimum="100" value="80" ></meter>
diff --git a/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic-vertical.html b/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic-vertical.html
index 82cf9c16..e277305 100644
--- a/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic-vertical.html
+++ b/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic-vertical.html
@@ -31,7 +31,7 @@
   <li>Check that padding is created:</li>
   <li>Check that margin is created:</li>
   <li>Check that box shadow is drawn:</li>
-  <li>Check that background CSS property disables -webkit-appearance:</li>
+  <li>Check that background CSS property disables appearance:</li>
 </ul>
 <ul>
   <meter style="border-color: #000000; border-style: solid; border-width: 5px;" min="0" max="100" low="30" high="60" optimum="100" value="80" ></meter>
diff --git a/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic.html b/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic.html
index c173872..79476b50 100644
--- a/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic.html
+++ b/third_party/blink/web_tests/fast/forms/color-scheme/meter/meter-appearance-basic.html
@@ -30,7 +30,7 @@
   <li>Check that padding is created: <meter style="border-color: #000000; padding: 5px;" min="0" max="100" low="30" high="60" optimum="100" value="80" ></meter></li>
   <li>Check that margin is created: <meter style="border-color: #000000; margin: 5px;" min="0" max="100" low="30" high="60" optimum="100" value="80" ></meter></li>
   <li>Check that box shadow is drawn: <meter style="box-shadow: 4px 4px 10px rgba(255,0,0,0.5), inset 4px 4px 4px rgba(0,255,0,0.5);"></meter></li>
-  <li>Check that background CSS property disables -webkit-appearance: <meter style="background: blue; color: white;" min="0" max="100" low="30" high="60" optimum="100" value="80"></meter></li>
+  <li>Check that background CSS property disables appearance: <meter style="background: blue; color: white;" min="0" max="100" low="30" high="60" optimum="100" value="80"></meter></li>
 </ul>
 <h2>Providing bar and/or value styles</h2>
 <ul>
diff --git a/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-color-function-expected.txt b/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-color-function-expected.txt
deleted file mode 100644
index feecea52..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-color-function-expected.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-This is a testharness.js-based test.
-Found 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] e.style['color'] = "color(srgb calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(srgb calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(srgb 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(srgb 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(srgb 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(srgb 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(srgb-linear calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(srgb-linear calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(srgb-linear 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(srgb-linear 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(srgb-linear 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(srgb-linear 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(a98-rgb calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(a98-rgb calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(a98-rgb 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(a98-rgb 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(a98-rgb 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(a98-rgb 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(rec2020 calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(rec2020 calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(rec2020 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(rec2020 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(rec2020 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(rec2020 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(prophoto-rgb calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(prophoto-rgb calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(prophoto-rgb 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(prophoto-rgb 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(prophoto-rgb 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(prophoto-rgb 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(display-p3 calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(display-p3 calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(display-p3 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(display-p3 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(display-p3 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(display-p3 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(xyz calc(0.5 + (sign(1em - 10px) * 0.1)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d65 calc(0.5 + (0.1 * sign(1em - 10px))) 0 0 / 0.5)" but got "color(xyz-d65 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(xyz 0.5 0 0 / calc(0.5 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d65 0.5 0 0 / calc(0.5 + (0.1 * sign(1em - 10px))))" but got "color(xyz-d65 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(xyz-d50 calc(0.5 + (sign(1em - 10px) * 0.1)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d50 calc(0.5 + (0.1 * sign(1em - 10px))) 0 0 / 0.5)" but got "color(xyz-d50 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(xyz-d50 0.5 0 0 / calc(0.5 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d50 0.5 0 0 / calc(0.5 + (0.1 * sign(1em - 10px))))" but got "color(xyz-d50 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(xyz-d65 calc(0.5 + (sign(1em - 10px) * 0.1)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d65 calc(0.5 + (0.1 * sign(1em - 10px))) 0 0 / 0.5)" but got "color(xyz-d65 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(xyz-d65 0.5 0 0 / calc(0.5 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d65 0.5 0 0 / calc(0.5 + (0.1 * sign(1em - 10px))))" but got "color(xyz-d65 0.5 0 0 / 0.4)"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-lab-expected.txt b/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-lab-expected.txt
deleted file mode 100644
index 63777f4..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-lab-expected.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-This is a testharness.js-based test.
-Found 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] e.style['color'] = "lab(calc(50 + (sign(1em - 10px) * 10)) 30 50 / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(calc(50 + (10 * sign(1em - 10px))) 30 50 / 50%)" but got "lab(40 30 50 / 0.5)"
-[FAIL] e.style['color'] = "oklab(calc(0.5 + (sign(1em - 10px) * 0.1)) 0.3 0.5 / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(calc(0.5 + (0.1 * sign(1em - 10px))) 0.3 0.5 / 50%)" but got "oklab(0.4 0.3 0.5 / 0.5)"
-[FAIL] e.style['color'] = "lab(60 30 50 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "lab(60 30 50 / calc(50% + (10% * sign(1em - 10px))))" but got "lab(60 30 50 / 0.4)"
-[FAIL] e.style['color'] = "oklab(0.6 0.3 0.5 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(0.6 0.3 0.5 / calc(50% + (10% * sign(1em - 10px))))" but got "oklab(0.6 0.3 0.5 / 0.4)"
-[FAIL] e.style['color'] = "lch(calc(50 + (sign(1em - 10px) * 10)) 30 50deg / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(calc(50 + (10 * sign(1em - 10px))) 30 50deg / 50%)" but got "lch(40 30 50 / 0.5)"
-[FAIL] e.style['color'] = "oklch(calc(0.5 + (sign(1em - 10px) * 0.1)) 0.3 50deg / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(calc(0.5 + (0.1 * sign(1em - 10px))) 0.3 50deg / 50%)" but got "oklch(0.4 0.3 50 / 0.5)"
-[FAIL] e.style['color'] = "lch(60 30 50deg / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "lch(60 30 50deg / calc(50% + (10% * sign(1em - 10px))))" but got "lch(60 30 50 / 0.4)"
-[FAIL] e.style['color'] = "oklch(0.6 0.3 50deg / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(0.6 0.3 50deg / calc(50% + (10% * sign(1em - 10px))))" but got "oklch(0.6 0.3 50 / 0.4)"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-rgb-expected.txt b/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-rgb-expected.txt
deleted file mode 100644
index c4fd42d..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-skia-graphite/external/wpt/css/css-color/parsing/color-valid-rgb-expected.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-This is a testharness.js-based test.
-Found 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] e.style['color'] = "rgb(calc(50% + (sign(1em - 10px) * 10%)), 0%, 0%, 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0% 0% / 50%)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(calc(50% + (sign(1em - 10px) * 10%)), 0%, 0%, 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0% 0% / 50%)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgb(calc(50 + (sign(1em - 10px) * 10)), 0, 0, 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50 + (10 * sign(1em - 10px))) 0 0 / 0.5)" but got "rgba(40, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(calc(50 + (sign(1em - 10px) * 10)), 0, 0, 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50 + (10 * sign(1em - 10px))) 0 0 / 0.5)" but got "rgba(40, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgb(0%, 0%, 0%, calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0% 0% / calc(50% + (10% * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.4)"
-[FAIL] e.style['color'] = "rgba(0%, 0%, 0%, calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0% 0% / calc(50% + (10% * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.4)"
-[FAIL] e.style['color'] = "rgb(0, 0, 0, calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0 0 0 / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-[FAIL] e.style['color'] = "rgba(0, 0, 0, calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0 0 0 / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-[FAIL] e.style['color'] = "rgb(calc(50% + (sign(1em - 10px) * 10%)) 0% 0% / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0% 0% / 50%)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(calc(50% + (sign(1em - 10px) * 10%)) 0% 0% / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0% 0% / 50%)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgb(calc(50 + (sign(1em - 10px) * 10)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50 + (10 * sign(1em - 10px))) 0 0 / 0.5)" but got "rgba(40, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(calc(50 + (sign(1em - 10px) * 10)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50 + (10 * sign(1em - 10px))) 0 0 / 0.5)" but got "rgba(40, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgb(0% 0% 0% / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0% 0% / calc(50% + (10% * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.4)"
-[FAIL] e.style['color'] = "rgba(0% 0% 0% / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0% 0% / calc(50% + (10% * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.4)"
-[FAIL] e.style['color'] = "rgb(0 0 0 / calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0 0 0 / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-[FAIL] e.style['color'] = "rgba(0 0 0 / calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0 0 0 / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-[FAIL] e.style['color'] = "rgba(calc(50% + (sign(1em - 10px) * 10%)) 0 0% / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0 0% / 0.5)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(0% 0 0% / calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0 0% / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/flag-specific/webgpu/http/tests/inspector-protocol/issues/gpuadapter-requestadapterinfo-deprecation-expected.txt b/third_party/blink/web_tests/flag-specific/webgpu/http/tests/inspector-protocol/issues/gpuadapter-requestadapterinfo-deprecation-expected.txt
new file mode 100644
index 0000000..6e0f47c8
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/webgpu/http/tests/inspector-protocol/issues/gpuadapter-requestadapterinfo-deprecation-expected.txt
@@ -0,0 +1,21 @@
+Tests that GPUAdapter requestAdapterInfo() deprecation issues are reported
+Inspector issue: {
+    issue : {
+        code : DeprecationIssue
+        details : {
+            deprecationIssueDetails : {
+                affectedFrame : {
+                    frameId : <string>
+                }
+                sourceCodeLocation : {
+                    columnNumber : 19
+                    lineNumber : 2
+                    scriptId : <string>
+                    url : 
+                }
+                type : V8GPUAdapter_RequestAdapterInfo_Method
+            }
+        }
+    }
+}
+
diff --git a/third_party/blink/web_tests/http/tests/expect-no-embedded-resources/no-preloadscanning-when-no-resources.php b/third_party/blink/web_tests/http/tests/expect-no-embedded-resources/no-preloadscanning-when-no-resources.php
new file mode 100644
index 0000000..4d1c4cc
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/expect-no-embedded-resources/no-preloadscanning-when-no-resources.php
@@ -0,0 +1,19 @@
+<?php
+header("Document-Policy: expect-no-embedded-resources");
+?>
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Preloads are not identified when Document Policy expect-no-embedded-resources is enabled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  var EXPECT_NO_EMDBEDDED_RESOURCES_COUNTER = 5100;
+  test(t => {
+    assert_false(internals.isPreloaded('resources/square20.png'), 'image was not preloaded');
+    assert_true(internals.isUseCounted(document, EXPECT_NO_EMDBEDDED_RESOURCES_COUNTER));
+  }, 'Resources are not preloaded when SkipPreloadScanning feature is enabled.');
+</script>
+<div>This test passes if the img src is not preloaded when SkipPreloadScanning feature is enabled.</div>
+<img src="resources/square20.png">
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/css/constructed-stylesheet-source-url.js b/third_party/blink/web_tests/http/tests/inspector-protocol/css/constructed-stylesheet-source-url.js
index 62d75ad4..83fe4a5 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/css/constructed-stylesheet-source-url.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/css/constructed-stylesheet-source-url.js
@@ -4,7 +4,7 @@
     const styleSheetFromConstructor = new CSSStyleSheet();
     styleSheetFromConstructor.replaceSync("div { background-color: 'blue' }");
 
-    import styleSheetFromModule from '../resources/css-module.php?url=css-module.css' assert { type: 'css' };
+    import styleSheetFromModule from '../resources/css-module.php?url=css-module.css' with { type: 'css' };
 
     document.adoptedStyleSheets = [styleSheetFromConstructor, styleSheetFromModule];
   </script>`, 'Check sourceURL of constructed stylesheets, from `new` and from CSS module import');
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/issues/gpuadapter-requestadapterinfo-deprecation.js b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/gpuadapter-requestadapterinfo-deprecation.js
new file mode 100644
index 0000000..0ae0395
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/issues/gpuadapter-requestadapterinfo-deprecation.js
@@ -0,0 +1,12 @@
+(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
+  const { session, dp } = await testRunner.startBlank(`Tests that GPUAdapter requestAdapterInfo() deprecation issues are reported`);
+  await dp.Audits.enable();
+  const promise = dp.Audits.onceIssueAdded();
+  session.evaluate(`(async function() {
+    const adapter = await navigator.gpu.requestAdapter();
+    await adapter.requestAdapterInfo();
+  })()`);
+  const result = await promise;
+  testRunner.log(result.params, "Inspector issue: ");
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html b/third_party/blink/web_tests/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html
index 0b9a7d7..692757ad 100644
--- a/third_party/blink/web_tests/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html
+++ b/third_party/blink/web_tests/http/tests/media/media-source/stream_memory_tests/mediasource-appendbuffer-quota-exceeded-default-buffers.html
@@ -12,8 +12,9 @@
               var subType = MediaSourceUtil.getSubType(MediaSourceUtil.AUDIO_ONLY_TYPE);
               var mediaDataManifest = subType + '/test-a-5min-44100Hz-1ch-manifest.json';
               MediaSourceUtil.fillUpSourceBuffer(test, mediaSource, mediaDataManifest, function (appendedDataSize) { // onBufferFull
+                  // This test is just measuring the amount of overhead StreamParserBuffer adds per packet.
                   assert_greater_than(appendedDataSize, 2 * 1048576, "Appended more than 2MB of data");
-                  assert_less_than(appendedDataSize, 3 * 1048576, "Appended less than 3MB of data");
+                  assert_less_than(appendedDataSize, 5 * 1048576, "Appended less than 5MB of data");
                   test.done();
               });
           }, 'Appending data repeatedly should fill up the buffer and throw a QuotaExceededError when buffer is full.');
diff --git a/third_party/blink/web_tests/platform/linux/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/linux/fast/dom/HTMLMeterElement/meter-styles-expected.png
index 1d6b089..6bffbbe1e 100644
--- a/third_party/blink/web_tests/platform/linux/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index d824a8c..511586c 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index f5c126be..a3ad4830 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index c178a87c..e910791 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index 5d16b880..d7557843 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index ad4c62f..c387a346 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 3247909..4278f7ef 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index fd1523d..33dc65f 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 4a105168..45c9ecd4 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 6091743..c1cae26 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
index 8bc9b13..acea7f7 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index a2dcf43d..2b8913f3a 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 89c3899e..a5630872 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 991d2d7..fa61b68 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
index 8bc9b13..acea7f7 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index a2dcf43d..2b8913f3a 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 89c3899e..a5630872 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 991d2d7..fa61b68 100644
--- a/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
index 8bc9b13..acea7f7 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index a2dcf43d..2b8913f3a 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 89c3899e..a5630872 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 991d2d7..fa61b68 100644
--- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
index 8bc9b13..acea7f7 100644
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac14-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index a2dcf43d..2b8913f3a 100644
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 89c3899e..a5630872 100644
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 991d2d7..fa61b68 100644
--- a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
index 8bc9b13..acea7f7 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index a2dcf43d..2b8913f3a 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 89c3899e..a5630872 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 991d2d7..fa61b68 100644
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/mac/fast/dom/HTMLMeterElement/meter-styles-expected.png
index 41fab84..af33b20 100644
--- a/third_party/blink/web_tests/platform/mac/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index f53c214..1c67d4ea 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 2fdb43bb..6285bae 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index fde545b..ea0e5ae9 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index 64a332e..11f2baa4 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 8d87ffba..6026fb2c 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index a3e52496..73583e37 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index 84a5a1f0..0d43cb5 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index cb3cfac..4baa4e0 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 538e97b..46ee89f5 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/win/fast/dom/HTMLMeterElement/meter-styles-expected.png
index 34e2740..442be26 100644
--- a/third_party/blink/web_tests/platform/win/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index 8ed00a3..1bc964f 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index d71101d..e5d5acdb 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 03bffc67..bf9c856 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index 7142cc5..1c1f9ba6 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 4946856..97677fb1 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index ea5ecad..2160db2 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index 69c3eb4a..68c4e7d 100644
--- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 9c40845..f3a3cdf 100644
--- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index 88b314ad..9bf5aaa 100644
--- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/matmul.https.any.worker_gpu-expected.txt b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/matmul.https.any.worker_gpu-expected.txt
deleted file mode 100644
index 74fbc44..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/matmul.https.any.worker_gpu-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] matmul float32 5D and 5D tensors
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': DirectML: The input tensor rank is larger than 4 for matmul operator."
-[FAIL] matmul float32 5D and 2D tensors
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': DirectML: The input tensor rank is larger than 4 for matmul operator."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/matmul.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/matmul.https.any_gpu-expected.txt
deleted file mode 100644
index 74fbc44..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/matmul.https.any_gpu-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] matmul float32 5D and 5D tensors
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': DirectML: The input tensor rank is larger than 4 for matmul operator."
-[FAIL] matmul float32 5D and 2D tensors
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': DirectML: The input tensor rank is larger than 4 for matmul operator."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-color-function-expected.txt b/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-color-function-expected.txt
deleted file mode 100644
index feecea52..0000000
--- a/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-color-function-expected.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-This is a testharness.js-based test.
-Found 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] e.style['color'] = "color(srgb calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(srgb calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(srgb 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(srgb 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(srgb 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(srgb 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(srgb-linear calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(srgb-linear calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(srgb-linear 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(srgb-linear 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(srgb-linear 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(srgb-linear 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(a98-rgb calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(a98-rgb calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(a98-rgb 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(a98-rgb 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(a98-rgb 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(a98-rgb 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(rec2020 calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(rec2020 calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(rec2020 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(rec2020 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(rec2020 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(rec2020 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(prophoto-rgb calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(prophoto-rgb calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(prophoto-rgb 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(prophoto-rgb 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(prophoto-rgb 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(prophoto-rgb 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(display-p3 calc(50% + (sign(1em - 10px) * 10%)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(display-p3 calc(50% + (10% * sign(1em - 10px))) 0 0 / 0.5)" but got "color(display-p3 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(display-p3 0.5 0 0 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(display-p3 0.5 0 0 / calc(50% + (10% * sign(1em - 10px))))" but got "color(display-p3 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(xyz calc(0.5 + (sign(1em - 10px) * 0.1)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d65 calc(0.5 + (0.1 * sign(1em - 10px))) 0 0 / 0.5)" but got "color(xyz-d65 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(xyz 0.5 0 0 / calc(0.5 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d65 0.5 0 0 / calc(0.5 + (0.1 * sign(1em - 10px))))" but got "color(xyz-d65 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(xyz-d50 calc(0.5 + (sign(1em - 10px) * 0.1)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d50 calc(0.5 + (0.1 * sign(1em - 10px))) 0 0 / 0.5)" but got "color(xyz-d50 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(xyz-d50 0.5 0 0 / calc(0.5 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d50 0.5 0 0 / calc(0.5 + (0.1 * sign(1em - 10px))))" but got "color(xyz-d50 0.5 0 0 / 0.4)"
-[FAIL] e.style['color'] = "color(xyz-d65 calc(0.5 + (sign(1em - 10px) * 0.1)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d65 calc(0.5 + (0.1 * sign(1em - 10px))) 0 0 / 0.5)" but got "color(xyz-d65 0.4 0 0 / 0.5)"
-[FAIL] e.style['color'] = "color(xyz-d65 0.5 0 0 / calc(0.5 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "color(xyz-d65 0.5 0 0 / calc(0.5 + (0.1 * sign(1em - 10px))))" but got "color(xyz-d65 0.5 0 0 / 0.4)"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-lab-expected.txt b/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-lab-expected.txt
deleted file mode 100644
index 63777f4..0000000
--- a/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-lab-expected.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-This is a testharness.js-based test.
-Found 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] e.style['color'] = "lab(calc(50 + (sign(1em - 10px) * 10)) 30 50 / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(calc(50 + (10 * sign(1em - 10px))) 30 50 / 50%)" but got "lab(40 30 50 / 0.5)"
-[FAIL] e.style['color'] = "oklab(calc(0.5 + (sign(1em - 10px) * 0.1)) 0.3 0.5 / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(calc(0.5 + (0.1 * sign(1em - 10px))) 0.3 0.5 / 50%)" but got "oklab(0.4 0.3 0.5 / 0.5)"
-[FAIL] e.style['color'] = "lab(60 30 50 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "lab(60 30 50 / calc(50% + (10% * sign(1em - 10px))))" but got "lab(60 30 50 / 0.4)"
-[FAIL] e.style['color'] = "oklab(0.6 0.3 0.5 / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(0.6 0.3 0.5 / calc(50% + (10% * sign(1em - 10px))))" but got "oklab(0.6 0.3 0.5 / 0.4)"
-[FAIL] e.style['color'] = "lch(calc(50 + (sign(1em - 10px) * 10)) 30 50deg / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(calc(50 + (10 * sign(1em - 10px))) 30 50deg / 50%)" but got "lch(40 30 50 / 0.5)"
-[FAIL] e.style['color'] = "oklch(calc(0.5 + (sign(1em - 10px) * 0.1)) 0.3 50deg / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(calc(0.5 + (0.1 * sign(1em - 10px))) 0.3 50deg / 50%)" but got "oklch(0.4 0.3 50 / 0.5)"
-[FAIL] e.style['color'] = "lch(60 30 50deg / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "lch(60 30 50deg / calc(50% + (10% * sign(1em - 10px))))" but got "lch(60 30 50 / 0.4)"
-[FAIL] e.style['color'] = "oklch(0.6 0.3 50deg / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(0.6 0.3 50deg / calc(50% + (10% * sign(1em - 10px))))" but got "oklch(0.6 0.3 50 / 0.4)"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-rgb-expected.txt b/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-rgb-expected.txt
deleted file mode 100644
index c4fd42d..0000000
--- a/third_party/blink/web_tests/platform/win10/external/wpt/css/css-color/parsing/color-valid-rgb-expected.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-This is a testharness.js-based test.
-Found 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] e.style['color'] = "rgb(calc(50% + (sign(1em - 10px) * 10%)), 0%, 0%, 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0% 0% / 50%)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(calc(50% + (sign(1em - 10px) * 10%)), 0%, 0%, 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0% 0% / 50%)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgb(calc(50 + (sign(1em - 10px) * 10)), 0, 0, 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50 + (10 * sign(1em - 10px))) 0 0 / 0.5)" but got "rgba(40, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(calc(50 + (sign(1em - 10px) * 10)), 0, 0, 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50 + (10 * sign(1em - 10px))) 0 0 / 0.5)" but got "rgba(40, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgb(0%, 0%, 0%, calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0% 0% / calc(50% + (10% * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.4)"
-[FAIL] e.style['color'] = "rgba(0%, 0%, 0%, calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0% 0% / calc(50% + (10% * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.4)"
-[FAIL] e.style['color'] = "rgb(0, 0, 0, calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0 0 0 / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-[FAIL] e.style['color'] = "rgba(0, 0, 0, calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0 0 0 / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-[FAIL] e.style['color'] = "rgb(calc(50% + (sign(1em - 10px) * 10%)) 0% 0% / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0% 0% / 50%)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(calc(50% + (sign(1em - 10px) * 10%)) 0% 0% / 50%)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0% 0% / 50%)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgb(calc(50 + (sign(1em - 10px) * 10)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50 + (10 * sign(1em - 10px))) 0 0 / 0.5)" but got "rgba(40, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(calc(50 + (sign(1em - 10px) * 10)) 0 0 / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50 + (10 * sign(1em - 10px))) 0 0 / 0.5)" but got "rgba(40, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgb(0% 0% 0% / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0% 0% / calc(50% + (10% * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.4)"
-[FAIL] e.style['color'] = "rgba(0% 0% 0% / calc(50% + (sign(1em - 10px) * 10%)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0% 0% / calc(50% + (10% * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.4)"
-[FAIL] e.style['color'] = "rgb(0 0 0 / calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0 0 0 / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-[FAIL] e.style['color'] = "rgba(0 0 0 / calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0 0 0 / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-[FAIL] e.style['color'] = "rgba(calc(50% + (sign(1em - 10px) * 10%)) 0 0% / 0.5)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(calc(50% + (10% * sign(1em - 10px))) 0 0% / 0.5)" but got "rgba(102, 0, 0, 0.5)"
-[FAIL] e.style['color'] = "rgba(0% 0 0% / calc(0.75 + (sign(1em - 10px) * 0.1)))" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(0% 0 0% / calc(0.75 + (0.1 * sign(1em - 10px))))" but got "rgba(0, 0, 0, 0.65)"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win11-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png b/third_party/blink/web_tests/platform/win11-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
index d1e4e009..bf7ca51c 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/fast/dom/HTMLMeterElement/meter-styles-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
index 5550ffe4..6bef4131 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
index 0e95887..c183f6c2 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
index d7849c5..511295c5e 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/meter/meter-appearance-basic-vertical-rtl-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
index a0dd3318..a1487a7 100644
--- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -244,6 +244,7 @@
 mask-repeat: repeat
 mask-size: auto
 mask-type: luminance
+masonry-slack: normal
 masonry-template-tracks: auto
 masonry-track-end: auto
 masonry-track-start: auto
diff --git a/third_party/blink/web_tests/virtual/css-relative-currentcolor-disabled/external/wpt/css/css-color/parsing/color-valid-relative-color-expected.txt b/third_party/blink/web_tests/virtual/css-relative-currentcolor-disabled/external/wpt/css/css-color/parsing/color-valid-relative-color-expected.txt
index 1c97ac6..36c65951 100644
--- a/third_party/blink/web_tests/virtual/css-relative-currentcolor-disabled/external/wpt/css/css-color/parsing/color-valid-relative-color-expected.txt
+++ b/third_party/blink/web_tests/virtual/css-relative-currentcolor-disabled/external/wpt/css/css-color/parsing/color-valid-relative-color-expected.txt
@@ -1,2132 +1,74 @@
 This is a testharness.js-based test.
-Found 1064 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from hsl(120deg 20% 50% / .5) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: rgb(from rgba(102, 153, 102, 0.5) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "rgb(from rgb(from rebeccapurple r g b) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rgb(from rebeccapurple r g b) r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rebeccapurple 0 0 0).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple   )" but got "color(srgb   )"
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: rgb(from rebeccapurple 0 0 0 / 0).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple    / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.2 0.6)\nExpected: rgb(from rebeccapurple 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0 0.6)\nExpected: rgb(from rebeccapurple r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0)\nExpected: rgb(from rebeccapurple r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 25 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.2 0.6)\nExpected: rgb(from rebeccapurple 25 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 25 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0980392 0.6)\nExpected: rgb(from rebeccapurple r 25 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0980392)\nExpected: rgb(from rebeccapurple r g 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / .25)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.25)\nExpected: rgb(from rebeccapurple r g b / 0.25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 25 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 25 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 25 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0980392 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 25 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.0980392 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g b / .20)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 0.20).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.6)\nExpected: rgb(from rebeccapurple 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.2)\nExpected: rgb(from rebeccapurple r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.2)\nExpected: rgb(from rebeccapurple r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 25 g b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.2 0.6 / 0.25)\nExpected: rgb(from rebeccapurple 25 g b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 25 b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0980392 0.6 / 0.25)\nExpected: rgb(from rebeccapurple r 25 b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g 25 / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0980392 / 0.25)\nExpected: rgb(from rebeccapurple r g 25 / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 25 g b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.4 0.6 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 25 g b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 25 b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0980392 0.6 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 25 b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r g 25 / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.0980392 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 25 / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.6 0.4)\nExpected: rgb(from rebeccapurple g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.00392157 0.4)\nExpected: rgb(from rebeccapurple b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4)\nExpected: rgb(from rebeccapurple r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.00392157 0.00392157 0.00392157)\nExpected: rgb(from rebeccapurple alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.00313725 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.00313725 0.00313725 0.00313725 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.00313725
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 20% 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r 20% 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r 10 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0392157 0.2)\nExpected: rgb(from rebeccapurple r 10 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple 0% 10 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.0392157 0.0392157)\nExpected: rgb(from rebeccapurple 0% 10 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 10 +/- 0.01, expected 10 but got 0.0392157
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 20% 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.0392157 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 20% 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) r 10 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0392157 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 10 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) 0% 10 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.0392157 0.0392157 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 0% 10 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "rgb(from rebeccapurple calc(r) calc(g) calc(b))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple calc(r) calc(g) calc(b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
+Found 35 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] e.style['color'] = "rgb(from rebeccapurple r calc(g * 2) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.0392157)\nExpected: rgb(from rebeccapurple r calc(2 * g) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(g * 2) 10)\nExpected: rgb(from rebeccapurple r calc(2 * g) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc( * g) )" but got "rgb(from rebeccapurple r calc(g * ) )"
 [FAIL] e.style['color'] = "rgb(from rebeccapurple b calc(r * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.0392157)\nExpected: rgb(from rebeccapurple b calc(0.5 * r) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple b calc(r * 0.5) 10)\nExpected: rgb(from rebeccapurple b calc(0.5 * r) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple b calc( * r) )" but got "rgb(from rebeccapurple b calc(r * ) )"
 [FAIL] e.style['color'] = "rgb(from rebeccapurple r calc(g * .5 + g * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r calc((0.5 * g) + (0.5 * g)) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.5 +/- 0.01, expected 0.5 but got 0.4
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(g * 0.5 + g * 0.5) 10)\nExpected: rgb(from rebeccapurple r calc((0.5 * g) + (0.5 * g)) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc(( * g) + ( * g)) )" but got "rgb(from rebeccapurple r calc(g *  + g * ) )"
 [FAIL] e.style['color'] = "rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r calc((0.5 * b) - (0.5 * g)) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.5 +/- 0.01, expected 0.5 but got 0.4
-[FAIL] e.style['color'] = "rgb(from rgb(20%, 40%, 60%, 80%) calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from rebeccapurple none none none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rebeccapurple none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / none)\nExpected: rgb(from rebeccapurple none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rebeccapurple r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / none)\nExpected: rgb(from rebeccapurple r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(20% 40% 60% / 80%) r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from rgb(20% 40% 60% / 80%) r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / none)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgb(from rgb(none none none) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rgb(0, 0, 0) r g b).\nError: assert_equals: Color format is correct. expected "rgb(from rgb(, , ) r g b)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "rgb(from rgb(none none none / none) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: rgb(from rgba(0, 0, 0, 0) r g b / alpha).\nError: assert_equals: Color format is correct. expected "rgb(from rgba(, , , ) r g b / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "rgb(from rgb(20% none 60%) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0 0.6)\nExpected: rgb(from rgb(51, 0, 153) r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgb(from rgb(20% 40% 60% / none) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: rgb(from rgba(51, 102, 153, 0) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(b * 0.5 - g * 0.5) 10)\nExpected: rgb(from rebeccapurple r calc((0.5 * b) - (0.5 * g)) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc(( * b) - ( * g)) )" but got "rgb(from rebeccapurple r calc(b *  - g * ) )"
 [FAIL] e.style['color'] = "rgb(from currentColor r g b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "rgb(from color-mix(in srgb, red, red) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: rgb(from color-mix(in srgb, red, red) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from hsl(120deg 20% 50% / .5) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: rgb(from rgba(102, 153, 102, 0.5) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "rgba(from rgb(from rebeccapurple r g b) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rgb(from rebeccapurple r g b) r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rebeccapurple 0 0 0).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple   )" but got "color(srgb   )"
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: rgb(from rebeccapurple 0 0 0 / 0).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple    / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.2 0.6)\nExpected: rgb(from rebeccapurple 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0 0.6)\nExpected: rgb(from rebeccapurple r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0)\nExpected: rgb(from rebeccapurple r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 25 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.2 0.6)\nExpected: rgb(from rebeccapurple 25 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 25 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0980392 0.6)\nExpected: rgb(from rebeccapurple r 25 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0980392)\nExpected: rgb(from rebeccapurple r g 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / .25)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.25)\nExpected: rgb(from rebeccapurple r g b / 0.25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 25 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 25 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 25 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0980392 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 25 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.0980392 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g b / .20)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 0.20).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.6)\nExpected: rgb(from rebeccapurple 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.2)\nExpected: rgb(from rebeccapurple r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.2)\nExpected: rgb(from rebeccapurple r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 25 g b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.2 0.6 / 0.25)\nExpected: rgb(from rebeccapurple 25 g b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 25 b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0980392 0.6 / 0.25)\nExpected: rgb(from rebeccapurple r 25 b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g 25 / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0980392 / 0.25)\nExpected: rgb(from rebeccapurple r g 25 / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 25 g b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.0980392 0.4 0.6 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 25 g b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 25 b / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0980392 0.6 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 25 b / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r g 25 / 25%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.0980392 / 0.25)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g 25 / 25%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.6 0.4)\nExpected: rgb(from rebeccapurple g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.00392157 0.4)\nExpected: rgb(from rebeccapurple b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4)\nExpected: rgb(from rebeccapurple r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.00392157 0.00392157 0.00392157)\nExpected: rgb(from rebeccapurple alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.00313725 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.2)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.00313725 0.00313725 0.00313725 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.00313725
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 20% 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r 20% 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r 10 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.0392157 0.2)\nExpected: rgb(from rebeccapurple r 10 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple 0% 10 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.0392157 0.0392157)\nExpected: rgb(from rebeccapurple 0% 10 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 10 +/- 0.01, expected 10 but got 0.0392157
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 20% 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.2 0.0392157 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 20% 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) r 10 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.0392157 0.2 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r 10 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) 0% 10 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.0392157 0.0392157 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) 0% 10 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "rgba(from rebeccapurple calc(r) calc(g) calc(b))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: rgb(from rebeccapurple calc(r) calc(g) calc(b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
 [FAIL] e.style['color'] = "rgba(from rebeccapurple r calc(g * 2) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.0392157)\nExpected: rgb(from rebeccapurple r calc(2 * g) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(g * 2) 10)\nExpected: rgb(from rebeccapurple r calc(2 * g) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc( * g) )" but got "rgb(from rebeccapurple r calc(g * ) )"
 [FAIL] e.style['color'] = "rgba(from rebeccapurple b calc(r * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.0392157)\nExpected: rgb(from rebeccapurple b calc(0.5 * r) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
+  Colors do not match.\nActual:   rgb(from rebeccapurple b calc(r * 0.5) 10)\nExpected: rgb(from rebeccapurple b calc(0.5 * r) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple b calc( * r) )" but got "rgb(from rebeccapurple b calc(r * ) )"
 [FAIL] e.style['color'] = "rgba(from rebeccapurple r calc(g * .5 + g * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r calc((0.5 * g) + (0.5 * g)) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.5 +/- 0.01, expected 0.5 but got 0.4
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(g * 0.5 + g * 0.5) 10)\nExpected: rgb(from rebeccapurple r calc((0.5 * g) + (0.5 * g)) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc(( * g) + ( * g)) )" but got "rgb(from rebeccapurple r calc(g *  + g * ) )"
 [FAIL] e.style['color'] = "rgba(from rebeccapurple r calc(b * .5 - g * .5) 10)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.0392157)\nExpected: rgb(from rebeccapurple r calc((0.5 * b) - (0.5 * g)) 10).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.5 +/- 0.01, expected 0.5 but got 0.4
-[FAIL] e.style['color'] = "rgba(from rgb(20%, 40%, 60%, 80%) calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from rebeccapurple none none none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rebeccapurple none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / none)\nExpected: rgb(from rebeccapurple none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0)\nExpected: rgb(from rebeccapurple r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rebeccapurple r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / none)\nExpected: rgb(from rebeccapurple r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(20% 40% 60% / 80%) r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0 / 0.8)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from rgb(20% 40% 60% / 80%) r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / none)\nExpected: rgb(from rgba(51, 102, 153, 0.8) r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "rgba(from rgb(none none none) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: rgb(from rgb(0, 0, 0) r g b).\nError: assert_equals: Color format is correct. expected "rgb(from rgb(, , ) r g b)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "rgba(from rgb(none none none / none) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: rgb(from rgba(0, 0, 0, 0) r g b / alpha).\nError: assert_equals: Color format is correct. expected "rgb(from rgba(, , , ) r g b / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "rgba(from rgb(20% none 60%) r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0 0.6)\nExpected: rgb(from rgb(51, 0, 153) r g b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "rgba(from rgb(20% 40% 60% / none) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: rgb(from rgba(51, 102, 153, 0) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
+  Colors do not match.\nActual:   rgb(from rebeccapurple r calc(b * 0.5 - g * 0.5) 10)\nExpected: rgb(from rebeccapurple r calc((0.5 * b) - (0.5 * g)) 10).\nError: assert_equals: Color format is correct. expected "rgb(from rebeccapurple r calc(( * b) - ( * g)) )" but got "rgb(from rebeccapurple r calc(b *  - g * ) )"
 [FAIL] e.style['color'] = "rgba(from currentColor r g b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "rgba(from color-mix(in srgb, red, red) r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: rgb(from color-mix(in srgb, red, red) r g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / .5) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hsl(from hsl(from rebeccapurple h s l) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from hsl(from rebeccapurple h s l) h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0 0% 0%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple 0 0% 0%).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple  % %)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0deg 0% 0%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple 0deg 0% 0%).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple deg % %)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0 0% 0% / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rebeccapurple 0 0% 0% / 0).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple  % % / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0deg 0% 0% / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rebeccapurple 0deg 0% 0% / 0).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple deg % % / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple 0 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 0deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple 0deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h 0% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4)\nExpected: hsl(from rebeccapurple h 0% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0)\nExpected: hsl(from rebeccapurple h s l / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) 0 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 0 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) 0deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 0deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h 0% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h 0% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s l / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 25 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2)\nExpected: hsl(from rebeccapurple 25 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple 25deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2)\nExpected: hsl(from rebeccapurple 25deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h 20% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.32 0.48)\nExpected: hsl(from rebeccapurple h 20% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.1 0.3)\nExpected: hsl(from rebeccapurple h s 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l / .25)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.25)\nExpected: hsl(from rebeccapurple h s l / .25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) 25 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 25 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) 25deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 25deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h 20% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.32 0.4 0.48 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h 20% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.1 0.2 0.3 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h s l / .2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / .2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h l s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.3 0.7)\nExpected: hsl(from rebeccapurple h l s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h alpha l / s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.396 0.404)\nExpected: hsl(from rebeccapurple h alpha l / s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h l l / l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.24 0.56)\nExpected: hsl(from rebeccapurple h l l / l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.01 0.0099 0.0101)\nExpected: hsl(from rebeccapurple h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h l s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3 0.5 0.7 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h l s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h alpha l / s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3968 0.4 0.4032)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h alpha l / s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h l l / l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.24 0.4 0.56)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h l l / l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.007936 0.008 0.008064 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.007936
-[FAIL] e.style['color'] = "hsl(from rebeccapurple calc(h) calc(s) calc(l))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple calc(h) calc(s) calc(l)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) calc(h) calc(s) calc(l) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hsl(from rebeccapurple none none none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / none)\nExpected: hsl(from rebeccapurple none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple h s l / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / none)\nExpected: hsl(from rebeccapurple h s l / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from rebeccapurple none s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple none s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / .5) h s none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / .5) h s l / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / none)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s l / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / .5) none s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.4 0.4 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) none s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.6
-[FAIL] e.style['color'] = "hsl(from hsl(none none none) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rgb(0, 0, 0) h s l).\nError: assert_equals: Color format is correct. expected "hsl(from rgb(, , ) h s l)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsl(from hsl(none none none / none) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rgba(0, 0, 0, 0) h s l / alpha).\nError: assert_equals: Color format is correct. expected "hsl(from rgba(, , , ) h s l / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsl(from hsl(120deg none 50% / .5) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.5 0.5 / 0.5)\nExpected: hsl(from rgba(128, 128, 128, 0.5) h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 128 +/- 0.01, expected 128 but got 0.5
-[FAIL] e.style['color'] = "hsl(from hsl(120deg 20% 50% / none) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0)\nExpected: hsl(from rgba(102, 153, 102, 0) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hsl(from hsl(none 20% 50% / .5) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.4 0.4 / 0.5)\nExpected: hsl(from rgba(153, 102, 102, 0.5) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 153 +/- 0.01, expected 153 but got 0.6
 [FAIL] e.style['color'] = "hsl(from currentColor h s l)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "hsl(from color-mix(in srgb, red, red) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: hsl(from color-mix(in srgb, red, red) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / .5) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hsla(from hsl(from rebeccapurple h s l) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from hsl(from rebeccapurple h s l) h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0 0% 0%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple 0 0% 0%).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple  % %)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0deg 0% 0%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple 0deg 0% 0%).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple deg % %)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0 0% 0% / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rebeccapurple 0 0% 0% / 0).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple  % % / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0deg 0% 0% / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rebeccapurple 0deg 0% 0% / 0).\nError: assert_equals: Color format is correct. expected "hsl(from rebeccapurple deg % % / )" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple 0 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 0deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple 0deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h 0% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4)\nExpected: hsl(from rebeccapurple h 0% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0)\nExpected: hsl(from rebeccapurple h s l / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) 0 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 0 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) 0deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 0deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h 0% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h 0% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s l / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 25 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2)\nExpected: hsl(from rebeccapurple 25 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple 25deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2)\nExpected: hsl(from rebeccapurple 25deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h 20% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.32 0.48)\nExpected: hsl(from rebeccapurple h 20% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.1 0.3)\nExpected: hsl(from rebeccapurple h s 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l / .25)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.25)\nExpected: hsl(from rebeccapurple h s l / .25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) 25 s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 25 s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) 25deg s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) 25deg s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h 20% l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.32 0.4 0.48 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h 20% l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.1 0.2 0.3 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h s l / .2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h s l / .2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h l s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.3 0.7)\nExpected: hsl(from rebeccapurple h l s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h alpha l / s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.396 0.404)\nExpected: hsl(from rebeccapurple h alpha l / s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h l l / l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.24 0.56)\nExpected: hsl(from rebeccapurple h l l / l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.01 0.0099 0.0101)\nExpected: hsl(from rebeccapurple h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h l s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3 0.5 0.7 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h l s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h alpha l / s)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3968 0.4 0.4032)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h alpha l / s).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h l l / l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.24 0.4 0.56)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h l l / l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.007936 0.008 0.008064 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.007936
-[FAIL] e.style['color'] = "hsla(from rebeccapurple calc(h) calc(s) calc(l))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6)\nExpected: hsl(from rebeccapurple calc(h) calc(s) calc(l)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hsl(from rgba(51, 102, 153, 0.8) calc(h) calc(s) calc(l) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hsla(from rebeccapurple none none none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / none)\nExpected: hsl(from rebeccapurple none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rebeccapurple h s none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple h s l / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / none)\nExpected: hsl(from rebeccapurple h s l / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from rebeccapurple none s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2)\nExpected: hsl(from rebeccapurple none s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / .5) h s none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / .5) h s l / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / none)\nExpected: hsl(from rgba(102, 153, 102, 0.5) h s l / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / .5) none s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.4 0.4 / 0.5)\nExpected: hsl(from rgba(102, 153, 102, 0.5) none s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.6
-[FAIL] e.style['color'] = "hsla(from hsl(none none none) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0)\nExpected: hsl(from rgb(0, 0, 0) h s l).\nError: assert_equals: Color format is correct. expected "hsl(from rgb(, , ) h s l)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "hsla(from hsl(none none none / none) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0 0 / 0)\nExpected: hsl(from rgba(0, 0, 0, 0) h s l / alpha).\nError: assert_equals: Color format is correct. expected "hsl(from rgba(, , , ) h s l / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "hsla(from hsl(120deg none 50% / .5) h s l)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.5 0.5 / 0.5)\nExpected: hsl(from rgba(128, 128, 128, 0.5) h s l).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 128 +/- 0.01, expected 128 but got 0.5
-[FAIL] e.style['color'] = "hsla(from hsl(120deg 20% 50% / none) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0)\nExpected: hsl(from rgba(102, 153, 102, 0) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hsla(from hsl(none 20% 50% / .5) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.4 0.4 / 0.5)\nExpected: hsl(from rgba(153, 102, 102, 0.5) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 153 +/- 0.01, expected 153 but got 0.6
 [FAIL] e.style['color'] = "hsla(from currentColor h s l)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "hsla(from color-mix(in srgb, red, red) h s l / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: hsl(from color-mix(in srgb, red, red) h s l / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w b)" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w b / alpha)" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hwb(from hsl(120deg 20% 50% / .5) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.4 / 0.5)\nExpected: hwb(from rgba(102, 153, 102, 0.5) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 102 +/- 0.01, expected 102 but got 0.4
-[FAIL] e.style['color'] = "hwb(from hwb(from rebeccapurple h w b) h w b)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from hwb(from rebeccapurple h w b) h w b)" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0 0% 0%)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0 0% 0%)" but got "color(srgb 1 0 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0deg 0% 0%)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0deg 0% 0%)" but got "color(srgb 1 0 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0 0% 0% / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0 0% 0% / 0)" but got "color(srgb 1 0 0 / 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0deg 0% 0% / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0deg 0% 0% / 0)" but got "color(srgb 1 0 0 / 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0 w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0 w b / alpha)" but got "color(srgb 0.6 0.2 0.2)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 0deg w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 0deg w b / alpha)" but got "color(srgb 0.6 0.2 0.2)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h 0% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h 0% b / alpha)" but got "color(srgb 0.3 0 0.6)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w 0% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w 0% / alpha)" but got "color(srgb 0.6 0.2 1)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w b / 0)" but got "color(srgb 0.4 0.2 0.6 / 0)"
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) 0 w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) 0 w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) 0deg w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.2 0.2 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) 0deg w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h 0% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.3 0.6 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h 0% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w 0% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.6 1 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w 0% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 25 w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 25 w b / alpha)" but got "color(srgb 0.6 0.366667 0.2)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple 25deg w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple 25deg w b / alpha)" but got "color(srgb 0.6 0.366667 0.2)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h 20% b / alpha)" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w 20% / alpha)" but got "color(srgb 0.5 0.2 0.8)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b / .2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.2 0.6 / 0.2)\nExpected: hwb(from rebeccapurple h w b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) 25 w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) 25 w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) 25deg w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.6 0.366667 0.2 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) 25deg w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.8 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w b / .2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.2)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w b / .2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h b w)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h b w)" but got "color(srgb 0.6 0.4 0.8)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h alpha w / b)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h alpha w / b)" but got "color(srgb 0.405 0.01 0.8)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w w / w)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w w / w)" but got "color(srgb 0.5 0.2 0.8)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h alpha alpha / alpha)" but got "color(srgb 0.5 0.01 0.99)"
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h b w)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.6 0.8 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h b w).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.4
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h alpha w / b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.008 0.404 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h alpha w / b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h w w / w)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h w w / w).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.008 0.5 0.992 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) h alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.008
-[FAIL] e.style['color'] = "hwb(from rebeccapurple calc(h) calc(w) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple calc(h) calc(w) calc(b))" but got "color(srgb 0.4 0.2 0.6)"
-[FAIL] e.style['color'] = "hwb(from rgb(20%, 40%, 60%, 80%) calc(h) calc(w) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.4 0.6 / 0.8)\nExpected: hwb(from rgba(51, 102, 153, 0.8) calc(h) calc(w) calc(b) / calc(alpha)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hwb(from rebeccapurple none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple none none none)" but got "color(srgb 1 0 0)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple none none none / none)" but got "color(srgb 1 0 0 / none)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w none)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w none)" but got "color(srgb 0.6 0.2 1)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w none / alpha)" but got "color(srgb 0.6 0.2 1)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple h w b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple h w b / none)" but got "color(srgb 0.4 0.2 0.6 / none)"
-[FAIL] e.style['color'] = "hwb(from rebeccapurple none w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from rebeccapurple none w b / alpha)" but got "color(srgb 0.6 0.2 0.2)"
-[FAIL] e.style['color'] = "hwb(from hwb(120deg 20% 50% / .5) h w none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 1 0.2 / 0.5)\nExpected: hwb(from rgba(51, 128, 51, 0.5) h w none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hwb(from hwb(120deg 20% 50% / .5) h w b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.2 / none)\nExpected: hwb(from rgba(51, 128, 51, 0.5) h w b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "hwb(from hwb(120deg 20% 50% / .5) none w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.2 0.2 / 0.5)\nExpected: hwb(from rgba(51, 128, 51, 0.5) none w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.5
-[FAIL] e.style['color'] = "hwb(from hwb(none none none) h w b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0)\nExpected: hwb(from rgb(255, 0, 0) h w b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 255 +/- 0.01, expected 255 but got 1
-[FAIL] e.style['color'] = "hwb(from hwb(none none none / none) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1 0 0 / 0)\nExpected: hwb(from rgba(255, 0, 0, 0) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 255 +/- 0.01, expected 255 but got 1
-[FAIL] e.style['color'] = "hwb(from hwb(120deg none 50% / .5) h w b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.5 0 / 0.5)\nExpected: hwb(from rgba(0, 128, 0, 0.5) h w b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 128 +/- 0.01, expected 128 but got 0.5
-[FAIL] e.style['color'] = "hwb(from hwb(120deg 20% 50% / none) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.2 / 0)\nExpected: hwb(from rgba(51, 128, 51, 0) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 51 +/- 0.01, expected 51 but got 0.2
-[FAIL] e.style['color'] = "hwb(from hwb(none 20% 50% / .5) h w b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.2 0.2 / 0.5)\nExpected: hwb(from rgba(128, 51, 51, 0.5) h w b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 128 +/- 0.01, expected 128 but got 0.5
 [FAIL] e.style['color'] = "hwb(from currentColor h w b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "hwb(from color-mix(in srgb, red, red) h w b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "hwb(from color-mix(in srgb, red, red) h w b / alpha)" but got "color(srgb 1 0 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a b)" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a b / alpha)" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l a b / alpha).\nError: assert_equals: Color format is correct. expected "lab(from lab(   / ) l a b / alpha)" but got "lab(   / )"
-[FAIL] e.style['color'] = "lab(from lab(200 300 400 / 500%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(100 300 400)\nExpected: lab(from lab(100 300 400) l a b / alpha).\nError: assert_equals: Color format is correct. expected "lab(from lab(  ) l a b / alpha)" but got "lab(  )"
-[FAIL] e.style['color'] = "lab(from lab(-200 -300 -400 / -500%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(0 -300 -400 / 0)\nExpected: lab(from lab(0 -300 -400 / 0) l a b / alpha).\nError: assert_equals: Color format is correct. expected "lab(from lab( - - / ) l a b / alpha)" but got "lab( - - / )"
-[FAIL] e.style['color'] = "lab(from lab(from lab(25 20 50) l a b) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(from lab(25 20 50) l a b) l a b)" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "lab(from color(display-p3 0 0 0) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from color(display-p3 0 0 0) l a b / alpha)" but got "lab(0 0 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) 0 0 0)" but got "lab(0 0 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) 0 0 0 / 0)" but got "lab(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) 0 a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) 0 a b / alpha)" but got "lab(0 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l 0 b / alpha)" but got "lab(25 0 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a 0 / alpha)" but got "lab(25 20 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a b / 0)" but got "lab(25 20 50 / 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) 0 a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(0 20 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) 0 a b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 0 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 0 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l a 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a b / 0)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0)\nExpected: lab(from lab(25 20 50 / 0.4) l a b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) 35 a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) 35 a b / alpha)" but got "lab(35 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l 35 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l 35 b / alpha)" but got "lab(25 35 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a 35 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a 35 / alpha)" but got "lab(25 20 35)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b / .35)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0.35)\nExpected: lab(from lab(25 20 50) l a b / 0.35).\nError: assert_equals: Color format is correct. expected "lab(from lab(  ) l a b / )" but got "lab(   / )"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) 35 a b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(35 20 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) 35 a b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l 35 b / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 35 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l 35 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a 35 / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 35 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l a 35 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a b / .35)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0.35)\nExpected: lab(from lab(25 20 50 / 0.4) l a b / .35).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lab(from lab(0.7 45 30 / 40%) 200 300 400 / 500)" should set the property value
-  Colors do not match.\nActual:   lab(100 300 400)\nExpected: lab(from lab(0.7 45 30 / 0.4) 200 300 400 / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "lab(from lab(0.7 45 30 / 40%) -200 -300 -400 / -500)" should set the property value
-  Colors do not match.\nActual:   lab(0 -300 -400 / 0)\nExpected: lab(from lab(0.7 45 30 / 0.4) -200 -300 -400 / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l b a)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l b a)" but got "lab(25 50 20)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a a / a)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a a / a)" but got "lab(25 20 20)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l b a)" should set the property value
-  Colors do not match.\nActual:   lab(25 50 20 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l b a).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 20 +/- 0.01, expected 20 but got 50
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a a / a)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 20)\nExpected: lab(from lab(25 20 50 / 0.4) l a a / a).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) calc(l) calc(a) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) calc(l) calc(a) calc(b))" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) calc(l) calc(a) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "lab(from lab(   / ) calc(l) calc(a) calc(b) / calc(alpha))" but got "lab(   / )"
-[FAIL] e.style['color'] = "lab(from lab(50 -30 40) calc(l - 20) a b)" should set the property value
-  Colors do not match.\nActual:   lab(30 -30 40)\nExpected: lab(from lab(50 -30 40) calc(-20 + l) a b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
 [FAIL] e.style['color'] = "lab(from lab(50 -30 40) l calc(a / 3) calc(b / 2))" should set the property value
-  Colors do not match.\nActual:   lab(50 -10 20)\nExpected: lab(from lab(50 -30 40) l calc(0.333333 * a) calc(0.5 * b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 3
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) none none none)" but got "lab(none none none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) none none none / none)" but got "lab(none none none / none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a none)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a none)" but got "lab(25 20 none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a none / alpha)" but got "lab(25 20 none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50) l a b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50) l a b / none)" but got "lab(25 20 50 / none)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a none / alpha)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 none / 0.4)\nExpected: lab(from lab(25 20 50 / 0.4) l a none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / 40%) l a b / none)" should set the property value
-  Colors do not match.\nActual:   lab(25 20 50 / none)\nExpected: lab(from lab(25 20 50 / 0.4) l a b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lab(from lab(none none none) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(none none none) l a b)" but got "lab(0 0 0)"
-[FAIL] e.style['color'] = "lab(from lab(none none none / none) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(none none none / none) l a b / alpha)" but got "lab(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lab(from lab(25 none 50) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 none 50) l a b)" but got "lab(25 0 50)"
-[FAIL] e.style['color'] = "lab(from lab(25 20 50 / none) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from lab(25 20 50 / none) l a b / alpha)" but got "lab(25 20 50 / 0)"
+  Colors do not match.\nActual:   lab(from lab(50 -30 40) l calc(a / 3) calc(b / 2))\nExpected: lab(from lab(50 -30 40) l calc(0.333333 * a) calc(0.5 * b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 0.333333 +/- 0.01, expected 0.333333 but got 3
 [FAIL] e.style['color'] = "lab(from currentColor l a b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "lab(from color-mix(in lab, lab(25 20 50), lab(25 20 50)) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lab(from color-mix(in lab, lab(25 20 50), lab(25 20 50)) l a b / alpha)" but got "lab(25 20 50)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b)" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b / alpha)" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a b / alpha).\nError: assert_equals: Color format is correct. expected "oklab(from oklab(   / ) l a b / alpha)" but got "oklab(   / )"
-[FAIL] e.style['color'] = "oklab(from oklab(2 3 4 / 500%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(1 3 4)\nExpected: oklab(from oklab(1 3 4) l a b / alpha).\nError: assert_equals: Color format is correct. expected "oklab(from oklab(  ) l a b / alpha)" but got "oklab(  )"
-[FAIL] e.style['color'] = "oklab(from oklab(-2 -3 -4 / -500%) l a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0 -3 -4 / 0)\nExpected: oklab(from oklab(0 -3 -4 / 0) l a b / alpha).\nError: assert_equals: Color format is correct. expected "oklab(from oklab( - - / ) l a b / alpha)" but got "oklab( - - / )"
-[FAIL] e.style['color'] = "oklab(from oklab(from oklab(0.25 0.2 0.5) l a b) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(from oklab(0.25 0.2 0.5) l a b) l a b)" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from color(display-p3 0 0 0) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from color(display-p3 0 0 0) l a b / alpha)" but got "oklab(0 0 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) 0 0 0)" but got "oklab(0 0 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) 0 0 0 / 0)" but got "oklab(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) 0 a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) 0 a b / alpha)" but got "oklab(0 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l 0 b / alpha)" but got "oklab(0.25 0 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a 0 / alpha)" but got "oklab(0.25 0.2 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b / 0)" but got "oklab(0.25 0.2 0.5 / 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) 0 a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0 0.2 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) 0 a b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a b / 0)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / 0)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) 0.35 a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) 0.35 a b / alpha)" but got "oklab(0.35 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l 0.35 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l 0.35 b / alpha)" but got "oklab(0.25 0.35 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a 0.35 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a 0.35 / alpha)" but got "oklab(0.25 0.2 0.35)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b / 0.35)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b / 0.35)" but got "oklab(0.25 0.2 0.5 / 0.35)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) 0.35 a b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.35 0.2 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) 0.35 a b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l 0.35 b / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.35 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l 0.35 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a 0.35 / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.35 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a 0.35 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a b / .35)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / 0.35)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a b / .35).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.7 0.45 0.3 / 40%) 2 3 4 / 500)" should set the property value
-  Colors do not match.\nActual:   oklab(1 3 4)\nExpected: oklab(from oklab(0.7 0.45 0.3 / 0.4) 2 3 4 / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "oklab(from oklab(0.7 0.45 0.3 / 40%) -2 -3 -4 / -500)" should set the property value
-  Colors do not match.\nActual:   oklab(0 -3 -4 / 0)\nExpected: oklab(from oklab(0.7 0.45 0.3 / 0.4) -2 -3 -4 / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l b a)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l b a)" but got "oklab(0.25 0.5 0.2)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a a / a)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a a / a)" but got "oklab(0.25 0.2 0.2 / 0.2)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l b a)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.5 0.2 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l b a).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.2 +/- 0.01, expected 0.2 but got 0.5
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a a / a)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.2 / 0.2)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a a / a).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 2, expected 0.5 +/- 0.01, expected 0.5 but got 0.2
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) calc(l) calc(a) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) calc(l) calc(a) calc(b))" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) calc(l) calc(a) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) calc(l) calc(a) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "oklab(from oklab(   / ) calc(l) calc(a) calc(b) / calc(alpha))" but got "oklab(   / )"
-[FAIL] e.style['color'] = "oklab(from oklab(0.7 0.25 -0.15) calc(l - 0.2) a b)" should set the property value
-  Colors do not match.\nActual:   oklab(0.5 0.25 -0.15)\nExpected: oklab(from oklab(0.7 0.25 -0.15) calc(-0.2 + l) a b).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
 [FAIL] e.style['color'] = "oklab(from oklab(0.7 0.25 -0.15) l calc(a / 2) calc(b / 3))" should set the property value
-  Colors do not match.\nActual:   oklab(0.7 0.125 -0.05)\nExpected: oklab(from oklab(0.7 0.25 -0.15) l calc(0.5 * a) calc(0.333333 * b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 3
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) none none none)" but got "oklab(none none none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) none none none / none)" but got "oklab(none none none / none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a none)" but got "oklab(0.25 0.2 none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a none / alpha)" but got "oklab(0.25 0.2 none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5) l a b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5) l a b / none)" but got "oklab(0.25 0.2 0.5 / none)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a none / alpha)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 none / 0.4)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / 40%) l a b / none)" should set the property value
-  Colors do not match.\nActual:   oklab(0.25 0.2 0.5 / none)\nExpected: oklab(from oklab(0.25 0.2 0.5 / 0.4) l a b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklab(from oklab(none none none) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(none none none) l a b)" but got "oklab(0 0 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(none none none / none) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(none none none / none) l a b / alpha)" but got "oklab(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 none 0.5) l a b)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 none 0.5) l a b)" but got "oklab(0.25 0 0.5)"
-[FAIL] e.style['color'] = "oklab(from oklab(0.25 0.2 0.5 / none) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from oklab(0.25 0.2 0.5 / none) l a b / alpha)" but got "oklab(0.25 0.2 0.5 / 0)"
+  Colors do not match.\nActual:   oklab(from oklab(0.7 0.25 -0.15) l calc(a / 2) calc(b / 3))\nExpected: oklab(from oklab(0.7 0.25 -0.15) l calc(0.5 * a) calc(0.333333 * b)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 0.5 +/- 0.01, expected 0.5 but got 2
 [FAIL] e.style['color'] = "oklab(from currentColor l a b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "oklab(from color-mix(in oklab, oklab(0.25 0.2 0.5), oklab(0.25 0.2 0.5)) l a b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklab(from color-mix(in oklab, oklab(0.25 0.2 0.5), oklab(0.25 0.2 0.5)) l a b / alpha)" but got "oklab(0.25 0.2 0.5)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h)" but got "lch(0.7 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h / alpha)" but got "lch(0.7 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c h / alpha).\nError: assert_equals: Color format is correct. expected "lch(from lch(   / ) l c h / alpha)" but got "lch(   / )"
-[FAIL] e.style['color'] = "lch(from lch(200 300 400 / 500%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(100 300 40)\nExpected: lch(from lch(100 300 40) l c h / alpha).\nError: assert_equals: Color format is correct. expected "lch(from lch(  ) l c h / alpha)" but got "lch(  )"
-[FAIL] e.style['color'] = "lch(from lch(-200 -300 -400 / -500%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0 0 320 / 0)\nExpected: lch(from lch(0 0 320 / 0) l c h / alpha).\nError: assert_equals: Color format is correct. expected "lch(from lch(   / ) l c h / alpha)" but got "lch(   / )"
-[FAIL] e.style['color'] = "lch(from lch(from lch(0.7 45 30) l c h) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(from lch(0.7 45 30) l c h) l c h)" but got "lch(0.7 45 30)"
-[FAIL] e.style['color'] = "lch(from color(display-p3 0 0 0) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from color(display-p3 0 0 0) l c h / alpha)" but got "lch(0 0 0)"
-[FAIL] e.style['color'] = "lch(from lab(0.7 45 30) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lab(0.7 45 30) l c h / alpha)" but got "lch(0.7 54.0833 33.6901)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 0 0)" but got "lch(0 0 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 0 0deg)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 0 0deg)" but got "lch(0 0 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 0 0 / 0)" but got "lch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 0 0deg / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 0 0deg / 0)" but got "lch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 0 c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 0 c h / alpha)" but got "lch(0 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l 0 h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l 0 h / alpha)" but got "lch(0.7 0 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c 0 / alpha)" but got "lch(0.7 45 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c 0deg / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c 0deg / alpha)" but got "lch(0.7 45 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h / 0)" but got "lch(0.7 45 30 / 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 0 c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0 45 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) 0 c h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l 0 h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 0 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l 0 h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 0 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c 0deg / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 0 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c 0deg / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c h / 0)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / 0)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c h / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) 25 c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) 25 c h / alpha)" but got "lch(25 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l 25 h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l 25 h / alpha)" but got "lch(0.7 25 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c 25 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c 25 / alpha)" but got "lch(0.7 45 25)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c 25deg / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c 25deg / alpha)" but got "lch(0.7 45 25)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h / 0.25)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h / 0.25)" but got "lch(0.7 45 30 / 0.25)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 25 c h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(25 45 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) 25 c h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l 25 h / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 25 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l 25 h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c 25 / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 25 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c 25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c 25deg / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 25 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c 25deg / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c h / .25)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / 0.25)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c h / .25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 200 300 400 / 500)" should set the property value
-  Colors do not match.\nActual:   lch(100 300 40)\nExpected: lch(from lch(0.7 45 30 / 0.4) 200 300 400 / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) -200 -300 -400 / -500)" should set the property value
-  Colors do not match.\nActual:   lch(0 0 320 / 0)\nExpected: lch(from lch(0.7 45 30 / 0.4) -200 -300 -400 / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 50 120 400deg / 500)" should set the property value
-  Colors do not match.\nActual:   lch(50 120 40)\nExpected: lch(from lch(0.7 45 30 / 0.4) 50 120 400deg / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) 50 120 -400deg / -500)" should set the property value
-  Colors do not match.\nActual:   lch(50 120 320 / 0)\nExpected: lch(from lch(0.7 45 30 / 0.4) 50 120 -400deg / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c c / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c c / alpha)" but got "lch(0.7 45 45)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c c / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 45 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c c / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 2, expected 30 +/- 0.01, expected 30 but got 45
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) calc(l) calc(c) calc(h))" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) calc(l) calc(c) calc(h))" but got "lch(0.7 45 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) calc(l) calc(c) calc(h) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "lch(from lch(   / ) calc(l) calc(c) calc(h) / calc(alpha))" but got "lch(   / )"
-[FAIL] e.style['color'] = "lch(from lch(50 100 300) calc(l - 20) c h)" should set the property value
-  Colors do not match.\nActual:   lch(30 100 300)\nExpected: lch(from lch(50 100 300) calc(-20 + l) c h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
 [FAIL] e.style['color'] = "lch(from lch(50 100 300) l calc(c / 2) h)" should set the property value
-  Colors do not match.\nActual:   lch(50 50 300)\nExpected: lch(from lch(50 100 300) l calc(0.5 * c) h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
+  Colors do not match.\nActual:   lch(from lch(50 100 300) l calc(c / 2) h)\nExpected: lch(from lch(50 100 300) l calc(0.5 * c) h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 0.5 +/- 0.01, expected 0.5 but got 2
 [FAIL] e.style['color'] = "lch(from lch(50 100 300) l c calc(h * 2.5))" should set the property value
-  Colors do not match.\nActual:   lch(50 100 30)\nExpected: lch(from lch(50 100 300) l c calc(2.5 * h)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) none none none)" but got "lch(none none none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) none none none / none)" but got "lch(none none none / none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c none)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c none)" but got "lch(0.7 45 none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c none / alpha)" but got "lch(0.7 45 none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30) l c h / none)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30) l c h / none)" but got "lch(0.7 45 30 / none)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c none / alpha)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 none / 0.4)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / 40%) l c h / none)" should set the property value
-  Colors do not match.\nActual:   lch(0.7 45 30 / none)\nExpected: lch(from lch(0.7 45 30 / 0.4) l c h / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "lch(from lch(none none none) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(none none none) l c h)" but got "lch(0 0 0)"
-[FAIL] e.style['color'] = "lch(from lch(none none none / none) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(none none none / none) l c h / alpha)" but got "lch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 none 30) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 none 30) l c h)" but got "lch(0.7 0 30)"
-[FAIL] e.style['color'] = "lch(from lch(0.7 45 30 / none) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from lch(0.7 45 30 / none) l c h / alpha)" but got "lch(0.7 45 30 / 0)"
+  Colors do not match.\nActual:   lch(from lch(50 100 300) l c calc(h * 2.5))\nExpected: lch(from lch(50 100 300) l c calc(2.5 * h)).\nError: assert_equals: Color format is correct. expected "lch(from lch(  ) l c calc( * h))" but got "lch(from lch(  ) l c calc(h * ))"
 [FAIL] e.style['color'] = "lch(from currentColor l c h)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "lch(from color-mix(in lch, lch(70 45 30), lch(70 45 30)) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "lch(from color-mix(in lch, lch(70 45 30), lch(70 45 30)) l c h / alpha)" but got "lch(70 45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h)" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h / alpha)" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c h / alpha).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(   / ) l c h / alpha)" but got "oklch(   / )"
-[FAIL] e.style['color'] = "oklch(from oklch(2 3 400 / 500%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(1 3 40)\nExpected: oklch(from oklch(1 3 40) l c h / alpha).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(  ) l c h / alpha)" but got "oklch(  )"
-[FAIL] e.style['color'] = "oklch(from oklch(-2 -3 -400 / -500%) l c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0 0 320 / 0)\nExpected: oklch(from oklch(0 0 320 / 0) l c h / alpha).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(   / ) l c h / alpha)" but got "oklch(   / )"
-[FAIL] e.style['color'] = "oklch(from oklch(from oklch(0.7 0.45 30) l c h) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(from oklch(0.7 0.45 30) l c h) l c h)" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from color(display-p3 0 0 0) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from color(display-p3 0 0 0) l c h / alpha)" but got "oklch(0 0 0)"
-[FAIL] e.style['color'] = "oklch(from oklab(0.7 45 30) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklab(0.7 45 30) l c h / alpha)" but got "oklch(0.7 54.0833 33.6901)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 0 0)" but got "oklch(0 0 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 0 0deg)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 0 0deg)" but got "oklch(0 0 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 0 0 / 0)" but got "oklch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 0 0deg / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 0 0deg / 0)" but got "oklch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0 c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0 c h / alpha)" but got "oklch(0 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l 0 h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l 0 h / alpha)" but got "oklch(0.7 0 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c 0 / alpha)" but got "oklch(0.7 0.45 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c 0deg / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c 0deg / alpha)" but got "oklch(0.7 0.45 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h / 0)" but got "oklch(0.7 0.45 30 / 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 0 c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0 0.45 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 0 c h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l 0 h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l 0 h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 0 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c 0deg / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 0 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c 0deg / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c h / 0)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / 0)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c h / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) 0.25 c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) 0.25 c h / alpha)" but got "oklch(0.25 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l 0.25 h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l 0.25 h / alpha)" but got "oklch(0.7 0.25 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c 0.25 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c 0.25 / alpha)" but got "oklch(0.7 0.45 0.25)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c 25deg / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c 25deg / alpha)" but got "oklch(0.7 0.45 25)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h / 0.25)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h / 0.25)" but got "oklch(0.7 0.45 30 / 0.25)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 0.25 c h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.25 0.45 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 0.25 c h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l 0.25 h / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.25 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l 0.25 h / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c 0.25 / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 0.25 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c 0.25 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c 25deg / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 25 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c 25deg / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c h / 0.25)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / 0.25)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c h / 0.25).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 2 3 400 / 500)" should set the property value
-  Colors do not match.\nActual:   oklch(1 3 40)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 2 3 400 / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) -2 -3 -400 / -500)" should set the property value
-  Colors do not match.\nActual:   oklch(0 0 320 / 0)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) -2 -3 -400 / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 0.5 1.2 400deg / 500)" should set the property value
-  Colors do not match.\nActual:   oklch(0.5 1.2 40)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 0.5 1.2 400deg / 500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) 0.5 1.2 -400deg / -500)" should set the property value
-  Colors do not match.\nActual:   oklch(0.5 1.2 320 / 0)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) 0.5 1.2 -400deg / -500).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 8 got 4
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c c / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c c / alpha)" but got "oklch(0.7 0.45 0.45)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c c / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 0.45 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c c / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 2, expected 30 +/- 0.01, expected 30 but got 0.45
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) calc(l) calc(c) calc(h))" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) calc(l) calc(c) calc(h))" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) calc(l) calc(c) calc(h) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(   / ) calc(l) calc(c) calc(h) / calc(alpha))" but got "oklch(   / )"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.2 300) calc(l - 0.2) c h)" should set the property value
-  Colors do not match.\nActual:   oklch(0.5 0.2 300)\nExpected: oklch(from oklch(0.7 0.2 300) calc(-0.2 + l) c h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
 [FAIL] e.style['color'] = "oklch(from oklch(0.7 0.2 300) l calc(c / 2) h)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.1 300)\nExpected: oklch(from oklch(0.7 0.2 300) l calc(0.5 * c) h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
+  Colors do not match.\nActual:   oklch(from oklch(0.7 0.2 300) l calc(c / 2) h)\nExpected: oklch(from oklch(0.7 0.2 300) l calc(0.5 * c) h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 0.5 +/- 0.01, expected 0.5 but got 2
 [FAIL] e.style['color'] = "oklch(from oklch(0.7 0.2 300) l c calc(h * 2.5))" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.2 30)\nExpected: oklch(from oklch(0.7 0.2 300) l c calc(2.5 * h)).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) none none none)" but got "oklch(none none none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) none none none / none)" but got "oklch(none none none / none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c none)" but got "oklch(0.7 0.45 none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c none / alpha)" but got "oklch(0.7 0.45 none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30) l c h / none)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30) l c h / none)" but got "oklch(0.7 0.45 30 / none)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c none / alpha)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 none / 0.4)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / 40%) l c h / none)" should set the property value
-  Colors do not match.\nActual:   oklch(0.7 0.45 30 / none)\nExpected: oklch(from oklch(0.7 0.45 30 / 0.4) l c h / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "oklch(from oklch(none none none) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(none none none) l c h)" but got "oklch(0 0 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(none none none / none) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(none none none / none) l c h / alpha)" but got "oklch(0 0 0 / 0)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 none 30) l c h)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 none 30) l c h)" but got "oklch(0.7 0 30)"
-[FAIL] e.style['color'] = "oklch(from oklch(0.7 0.45 30 / none) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from oklch(0.7 0.45 30 / none) l c h / alpha)" but got "oklch(0.7 0.45 30 / 0)"
+  Colors do not match.\nActual:   oklch(from oklch(0.7 0.2 300) l c calc(h * 2.5))\nExpected: oklch(from oklch(0.7 0.2 300) l c calc(2.5 * h)).\nError: assert_equals: Color format is correct. expected "oklch(from oklch(  ) l c calc( * h))" but got "oklch(from oklch(  ) l c calc(h * ))"
 [FAIL] e.style['color'] = "oklch(from currentColor l c h)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "oklch(from color-mix(in oklch, oklch(0.7 0.45 30), oklch(0.7 0.45 30)) l c h / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "oklch(from color-mix(in oklch, oklch(0.7 0.45 30), oklch(0.7 0.45 30)) l c h / alpha)" but got "oklch(0.7 0.45 30)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b)" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / alpha)" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb    / ) srgb r g b)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb    / ) srgb r g b / alpha)" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "color(from color(from color(srgb 0.7 0.5 0.3) srgb r g b) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(srgb 0.7 0.5 0.3) srgb r g b) srgb r g b)" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 0 0 0)" but got "color(srgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 0 0 0 / 0)" but got "color(srgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 0 g b / alpha)" but got "color(srgb 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r 0 b / alpha)" but got "color(srgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g 0 / alpha)" but got "color(srgb 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 0)" but got "color(srgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 0.2 g b / alpha)" but got "color(srgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 20% g b / alpha)" but got "color(srgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r 0.2 b / alpha)" but got "color(srgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r 20% b / alpha)" but got "color(srgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g 0.2 / alpha)" but got "color(srgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g 20% / alpha)" but got "color(srgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 0.2)" but got "color(srgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / 20%)" but got "color(srgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 2 3 4)" but got "color(srgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 2 3 4 / 5)" but got "color(srgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb -2 -3 -4)" but got "color(srgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb -2 -3 -4 / -5)" but got "color(srgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 200% 300% 400%)" but got "color(srgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb 200% 300% 400% / 500%)" but got "color(srgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb -200% -300% -400%)" but got "color(srgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb -200% -300% -400% / -500%)" but got "color(srgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb g b r)" but got "color(srgb 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb b alpha r / g)" but got "color(srgb 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r r r / r)" but got "color(srgb 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb alpha alpha alpha / alpha)" but got "color(srgb 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(srgb 1.7 1.5 1.3) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 1.7 1.5 1.3) srgb r g b)" but got "color(srgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 1.7 1.5 1.3) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 1.7 1.5 1.3) srgb r g b / alpha)" but got "color(srgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 1.7 1.5 1.3 / 140%) srgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1.7 1.5 1.3)\nExpected: color(from color(srgb 1.7 1.5 1.3) srgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb   ) srgb r g b)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "color(from color(srgb 1.7 1.5 1.3 / 140%) srgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 1.7 1.5 1.3)\nExpected: color(from color(srgb 1.7 1.5 1.3) srgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb   ) srgb r g b / alpha)" but got "color(srgb   )"
-[FAIL] e.style['color'] = "color(from color(srgb -0.7 -0.5 -0.3) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb -0.7 -0.5 -0.3) srgb r g b)" but got "color(srgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb -0.7 -0.5 -0.3) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb -0.7 -0.5 -0.3) srgb r g b / alpha)" but got "color(srgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb -0.7 -0.5 -0.3 / -40%) srgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(srgb -0.7 -0.5 -0.3 / 0) srgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb - - - / ) srgb r g b)" but got "color(srgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(srgb -0.7 -0.5 -0.3 / -40%) srgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(srgb -0.7 -0.5 -0.3 / 0) srgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb - - - / ) srgb r g b / alpha)" but got "color(srgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb calc(r) calc(g) calc(b))" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(srgb    / ) srgb calc(r) calc(g) calc(b) / calc(alpha))" but got "color(srgb    / )"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb none none none)" but got "color(srgb none none none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb none none none / none)" but got "color(srgb none none none / none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g none)" but got "color(srgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g none / alpha)" but got "color(srgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3) srgb r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3) srgb r g b / none)" but got "color(srgb 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 none / 0.4)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / 40%) srgb r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb 0.7 0.5 0.3 / none)\nExpected: color(from color(srgb 0.7 0.5 0.3 / 0.4) srgb r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(srgb none none none) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb none none none) srgb r g b)" but got "color(srgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(srgb none none none / none) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb none none none / none) srgb r g b / alpha)" but got "color(srgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 none 0.3) srgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 none 0.3) srgb r g b)" but got "color(srgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb 0.7 0.5 0.3 / none) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb 0.7 0.5 0.3 / none) srgb r g b / alpha)" but got "color(srgb 0.7 0.5 0.3 / 0)"
 [FAIL] e.style['color'] = "color(from currentColor srgb r g b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in srgb, color(srgb 0.7 0.5 0.3), color(srgb 0.7 0.5 0.3)) srgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in srgb, color(srgb 0.7 0.5 0.3), color(srgb 0.7 0.5 0.3)) srgb r g b / alpha)" but got "color(srgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b)" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / alpha)" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear    / ) srgb-linear r g b)" but got "color(srgb-linear    / )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear    / ) srgb-linear r g b / alpha)" but got "color(srgb-linear    / )"
-[FAIL] e.style['color'] = "color(from color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b) srgb-linear r g b)" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 0 0)" but got "color(srgb-linear 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 0 0 / 0)" but got "color(srgb-linear 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0 g b / alpha)" but got "color(srgb-linear 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 0 b / alpha)" but got "color(srgb-linear 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 0 / alpha)" but got "color(srgb-linear 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 0)" but got "color(srgb-linear 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 0.2 g b / alpha)" but got "color(srgb-linear 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 20% g b / alpha)" but got "color(srgb-linear 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 0.2 b / alpha)" but got "color(srgb-linear 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r 20% b / alpha)" but got "color(srgb-linear 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 0.2 / alpha)" but got "color(srgb-linear 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g 20% / alpha)" but got "color(srgb-linear 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 0.2)" but got "color(srgb-linear 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / 20%)" but got "color(srgb-linear 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 2 3 4)" but got "color(srgb-linear 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 2 3 4 / 5)" but got "color(srgb-linear 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -2 -3 -4)" but got "color(srgb-linear -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -2 -3 -4 / -5)" but got "color(srgb-linear -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 200% 300% 400%)" but got "color(srgb-linear 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear 200% 300% 400% / 500%)" but got "color(srgb-linear 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -200% -300% -400%)" but got "color(srgb-linear -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear -200% -300% -400% / -500%)" but got "color(srgb-linear -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear g b r)" but got "color(srgb-linear 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear b alpha r / g)" but got "color(srgb-linear 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r r r / r)" but got "color(srgb-linear 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear alpha alpha alpha / alpha)" but got "color(srgb-linear 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear g b r)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b)" but got "color(srgb-linear 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b / alpha)" but got "color(srgb-linear 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 1.7 1.5 1.3 / 140%) srgb-linear r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 1.7 1.5 1.3)\nExpected: color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear   ) srgb-linear r g b)" but got "color(srgb-linear   )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 1.7 1.5 1.3 / 140%) srgb-linear r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 1.7 1.5 1.3)\nExpected: color(from color(srgb-linear 1.7 1.5 1.3) srgb-linear r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear   ) srgb-linear r g b / alpha)" but got "color(srgb-linear   )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear -0.7 -0.5 -0.3) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear -0.7 -0.5 -0.3) srgb-linear r g b)" but got "color(srgb-linear -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear -0.7 -0.5 -0.3) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear -0.7 -0.5 -0.3) srgb-linear r g b / alpha)" but got "color(srgb-linear -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear -0.7 -0.5 -0.3 / -40%) srgb-linear r g b)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(srgb-linear -0.7 -0.5 -0.3 / 0) srgb-linear r g b).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear - - - / ) srgb-linear r g b)" but got "color(srgb-linear - - - / )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear -0.7 -0.5 -0.3 / -40%) srgb-linear r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(srgb-linear -0.7 -0.5 -0.3 / 0) srgb-linear r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear - - - / ) srgb-linear r g b / alpha)" but got "color(srgb-linear - - - / )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear calc(r) calc(g) calc(b))" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(srgb-linear    / ) srgb-linear calc(r) calc(g) calc(b) / calc(alpha))" but got "color(srgb-linear    / )"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear none none none)" but got "color(srgb-linear none none none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear none none none / none)" but got "color(srgb-linear none none none / none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g none)" but got "color(srgb-linear 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g none / alpha)" but got "color(srgb-linear 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3) srgb-linear r g b / none)" but got "color(srgb-linear 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 none / 0.4)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / 40%) srgb-linear r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(srgb-linear 0.7 0.5 0.3 / none)\nExpected: color(from color(srgb-linear 0.7 0.5 0.3 / 0.4) srgb-linear r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(srgb-linear none none none) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear none none none) srgb-linear r g b)" but got "color(srgb-linear 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear none none none / none) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear none none none / none) srgb-linear r g b / alpha)" but got "color(srgb-linear 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 none 0.3) srgb-linear r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 none 0.3) srgb-linear r g b)" but got "color(srgb-linear 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(srgb-linear 0.7 0.5 0.3 / none) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(srgb-linear 0.7 0.5 0.3 / none) srgb-linear r g b / alpha)" but got "color(srgb-linear 0.7 0.5 0.3 / 0)"
 [FAIL] e.style['color'] = "color(from currentColor srgb-linear r g b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in srgb-linear, color(srgb-linear 0.7 0.5 0.3), color(srgb-linear 0.7 0.5 0.3)) srgb-linear r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in srgb-linear, color(srgb-linear 0.7 0.5 0.3), color(srgb-linear 0.7 0.5 0.3)) srgb-linear r g b / alpha)" but got "color(srgb-linear 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b)" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / alpha)" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb    / ) a-rgb r g b)" but got "color(a-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb    / ) a-rgb r g b / alpha)" but got "color(a-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b) a98-rgb r g b)" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 0 0)" but got "color(a98-rgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 0 0 / 0)" but got "color(a98-rgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0 g b / alpha)" but got "color(a98-rgb 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 0 b / alpha)" but got "color(a98-rgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 0 / alpha)" but got "color(a98-rgb 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 0)" but got "color(a98-rgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 0.2 g b / alpha)" but got "color(a98-rgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 20% g b / alpha)" but got "color(a98-rgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 0.2 b / alpha)" but got "color(a98-rgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r 20% b / alpha)" but got "color(a98-rgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 0.2 / alpha)" but got "color(a98-rgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g 20% / alpha)" but got "color(a98-rgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 0.2)" but got "color(a98-rgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / 20%)" but got "color(a98-rgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 2 3 4)" but got "color(a98-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 2 3 4 / 5)" but got "color(a98-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -2 -3 -4)" but got "color(a98-rgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -2 -3 -4 / -5)" but got "color(a98-rgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 200% 300% 400%)" but got "color(a98-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb 200% 300% 400% / 500%)" but got "color(a98-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -200% -300% -400%)" but got "color(a98-rgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb -200% -300% -400% / -500%)" but got "color(a98-rgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb g b r)" but got "color(a98-rgb 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb b alpha r / g)" but got "color(a98-rgb 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r r r / r)" but got "color(a98-rgb 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb alpha alpha alpha / alpha)" but got "color(a98-rgb 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb g b r)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b)" but got "color(a98-rgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b / alpha)" but got "color(a98-rgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 1.7 1.5 1.3 / 140%) a98-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 1.7 1.5 1.3)\nExpected: color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb   ) a-rgb r g b)" but got "color(a-rgb   )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 1.7 1.5 1.3 / 140%) a98-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 1.7 1.5 1.3)\nExpected: color(from color(a98-rgb 1.7 1.5 1.3) a98-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb   ) a-rgb r g b / alpha)" but got "color(a-rgb   )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb -0.7 -0.5 -0.3) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb -0.7 -0.5 -0.3) a98-rgb r g b)" but got "color(a98-rgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb -0.7 -0.5 -0.3) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb -0.7 -0.5 -0.3) a98-rgb r g b / alpha)" but got "color(a98-rgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb -0.7 -0.5 -0.3 / -40%) a98-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(a98-rgb -0.7 -0.5 -0.3 / 0) a98-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb - - - / ) a-rgb r g b)" but got "color(a-rgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb -0.7 -0.5 -0.3 / -40%) a98-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(a98-rgb -0.7 -0.5 -0.3 / 0) a98-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb - - - / ) a-rgb r g b / alpha)" but got "color(a-rgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb calc(r) calc(g) calc(b))" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(a-rgb    / ) a-rgb calc(r) calc(g) calc(b) / calc(alpha))" but got "color(a-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb none none none)" but got "color(a98-rgb none none none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb none none none / none)" but got "color(a98-rgb none none none / none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g none)" but got "color(a98-rgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g none / alpha)" but got "color(a98-rgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3) a98-rgb r g b / none)" but got "color(a98-rgb 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 none / 0.4)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / 40%) a98-rgb r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(a98-rgb 0.7 0.5 0.3 / none)\nExpected: color(from color(a98-rgb 0.7 0.5 0.3 / 0.4) a98-rgb r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(a98-rgb none none none) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb none none none) a98-rgb r g b)" but got "color(a98-rgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb none none none / none) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb none none none / none) a98-rgb r g b / alpha)" but got "color(a98-rgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 none 0.3) a98-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 none 0.3) a98-rgb r g b)" but got "color(a98-rgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(a98-rgb 0.7 0.5 0.3 / none) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(a98-rgb 0.7 0.5 0.3 / none) a98-rgb r g b / alpha)" but got "color(a98-rgb 0.7 0.5 0.3 / 0)"
 [FAIL] e.style['color'] = "color(from currentColor a98-rgb r g b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in a98-rgb, color(a98-rgb 0.7 0.5 0.3), color(a98-rgb 0.7 0.5 0.3)) a98-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in a98-rgb, color(a98-rgb 0.7 0.5 0.3), color(a98-rgb 0.7 0.5 0.3)) a98-rgb r g b / alpha)" but got "color(a98-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b)" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / alpha)" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(rec    / ) rec r g b)" but got "color(rec    / )"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(rec    / ) rec r g b / alpha)" but got "color(rec    / )"
-[FAIL] e.style['color'] = "color(from color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b) rec2020 r g b)" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 0 0)" but got "color(rec2020 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 0 0 / 0)" but got "color(rec2020 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 0 g b / alpha)" but got "color(rec2020 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 0 b / alpha)" but got "color(rec2020 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 0 / alpha)" but got "color(rec2020 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 0)" but got "color(rec2020 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 0.2 g b / alpha)" but got "color(rec2020 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 20% g b / alpha)" but got "color(rec2020 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 0.2 b / alpha)" but got "color(rec2020 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r 20% b / alpha)" but got "color(rec2020 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 0.2 / alpha)" but got "color(rec2020 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g 20% / alpha)" but got "color(rec2020 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 0.2)" but got "color(rec2020 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / 20%)" but got "color(rec2020 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 2 3 4)" but got "color(rec2020 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 2 3 4 / 5)" but got "color(rec2020 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 -2 -3 -4)" but got "color(rec2020 -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 -2 -3 -4 / -5)" but got "color(rec2020 -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 200% 300% 400%)" but got "color(rec2020 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 200% 300% 400% / 500%)" but got "color(rec2020 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 -200% -300% -400%)" but got "color(rec2020 -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 -200% -300% -400% / -500%)" but got "color(rec2020 -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 g b r)" but got "color(rec2020 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 b alpha r / g)" but got "color(rec2020 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r r r / r)" but got "color(rec2020 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 alpha alpha alpha / alpha)" but got "color(rec2020 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 g b r)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b)" but got "color(rec2020 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b / alpha)" but got "color(rec2020 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 1.7 1.5 1.3 / 140%) rec2020 r g b)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 1.7 1.5 1.3)\nExpected: color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(rec   ) rec r g b)" but got "color(rec   )"
-[FAIL] e.style['color'] = "color(from color(rec2020 1.7 1.5 1.3 / 140%) rec2020 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 1.7 1.5 1.3)\nExpected: color(from color(rec2020 1.7 1.5 1.3) rec2020 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(rec   ) rec r g b / alpha)" but got "color(rec   )"
-[FAIL] e.style['color'] = "color(from color(rec2020 -0.7 -0.5 -0.3) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 -0.7 -0.5 -0.3) rec2020 r g b)" but got "color(rec2020 -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 -0.7 -0.5 -0.3) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 -0.7 -0.5 -0.3) rec2020 r g b / alpha)" but got "color(rec2020 -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 -0.7 -0.5 -0.3 / -40%) rec2020 r g b)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(rec2020 -0.7 -0.5 -0.3 / 0) rec2020 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(rec - - - / ) rec r g b)" but got "color(rec - - - / )"
-[FAIL] e.style['color'] = "color(from color(rec2020 -0.7 -0.5 -0.3 / -40%) rec2020 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(rec2020 -0.7 -0.5 -0.3 / 0) rec2020 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(rec - - - / ) rec r g b / alpha)" but got "color(rec - - - / )"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 calc(r) calc(g) calc(b))" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(rec    / ) rec calc(r) calc(g) calc(b) / calc(alpha))" but got "color(rec    / )"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 none none none)" but got "color(rec2020 none none none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 none none none / none)" but got "color(rec2020 none none none / none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g none)" but got "color(rec2020 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g none / alpha)" but got "color(rec2020 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3) rec2020 r g b / none)" but got "color(rec2020 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 none / 0.4)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / 40%) rec2020 r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(rec2020 0.7 0.5 0.3 / none)\nExpected: color(from color(rec2020 0.7 0.5 0.3 / 0.4) rec2020 r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(rec2020 none none none) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 none none none) rec2020 r g b)" but got "color(rec2020 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 none none none / none) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 none none none / none) rec2020 r g b / alpha)" but got "color(rec2020 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 none 0.3) rec2020 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 none 0.3) rec2020 r g b)" but got "color(rec2020 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(rec2020 0.7 0.5 0.3 / none) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(rec2020 0.7 0.5 0.3 / none) rec2020 r g b / alpha)" but got "color(rec2020 0.7 0.5 0.3 / 0)"
 [FAIL] e.style['color'] = "color(from currentColor rec2020 r g b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in rec2020, color(rec2020 0.7 0.5 0.3), color(rec2020 0.7 0.5 0.3)) rec2020 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in rec2020, color(rec2020 0.7 0.5 0.3), color(rec2020 0.7 0.5 0.3)) rec2020 r g b / alpha)" but got "color(rec2020 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b)" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb    / ) prophoto-rgb r g b)" but got "color(prophoto-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb    / ) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b) prophoto-rgb r g b)" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 0 0)" but got "color(prophoto-rgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 0 0 / 0)" but got "color(prophoto-rgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0 g b / alpha)" but got "color(prophoto-rgb 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 0 b / alpha)" but got "color(prophoto-rgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 0 / alpha)" but got "color(prophoto-rgb 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 0)" but got "color(prophoto-rgb 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 0.2 g b / alpha)" but got "color(prophoto-rgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 20% g b / alpha)" but got "color(prophoto-rgb 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 0.2 b / alpha)" but got "color(prophoto-rgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r 20% b / alpha)" but got "color(prophoto-rgb 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 0.2 / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g 20% / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 0.2)" but got "color(prophoto-rgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / 20%)" but got "color(prophoto-rgb 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 2 3 4)" but got "color(prophoto-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 2 3 4 / 5)" but got "color(prophoto-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -2 -3 -4)" but got "color(prophoto-rgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -2 -3 -4 / -5)" but got "color(prophoto-rgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 200% 300% 400%)" but got "color(prophoto-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb 200% 300% 400% / 500%)" but got "color(prophoto-rgb 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -200% -300% -400%)" but got "color(prophoto-rgb -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb -200% -300% -400% / -500%)" but got "color(prophoto-rgb -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb g b r)" but got "color(prophoto-rgb 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb b alpha r / g)" but got "color(prophoto-rgb 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r r r / r)" but got "color(prophoto-rgb 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb alpha alpha alpha / alpha)" but got "color(prophoto-rgb 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb g b r)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b)" but got "color(prophoto-rgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 1.7 1.5 1.3 / 140%) prophoto-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 1.7 1.5 1.3)\nExpected: color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb   ) prophoto-rgb r g b)" but got "color(prophoto-rgb   )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 1.7 1.5 1.3 / 140%) prophoto-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 1.7 1.5 1.3)\nExpected: color(from color(prophoto-rgb 1.7 1.5 1.3) prophoto-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb   ) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb   )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb -0.7 -0.5 -0.3) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb -0.7 -0.5 -0.3) prophoto-rgb r g b)" but got "color(prophoto-rgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb -0.7 -0.5 -0.3) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb -0.7 -0.5 -0.3) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb -0.7 -0.5 -0.3 / -40%) prophoto-rgb r g b)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(prophoto-rgb -0.7 -0.5 -0.3 / 0) prophoto-rgb r g b).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb - - - / ) prophoto-rgb r g b)" but got "color(prophoto-rgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb -0.7 -0.5 -0.3 / -40%) prophoto-rgb r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(prophoto-rgb -0.7 -0.5 -0.3 / 0) prophoto-rgb r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb - - - / ) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb - - - / )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb calc(r) calc(g) calc(b))" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(prophoto-rgb    / ) prophoto-rgb calc(r) calc(g) calc(b) / calc(alpha))" but got "color(prophoto-rgb    / )"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb none none none)" but got "color(prophoto-rgb none none none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb none none none / none)" but got "color(prophoto-rgb none none none / none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g none)" but got "color(prophoto-rgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g none / alpha)" but got "color(prophoto-rgb 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3) prophoto-rgb r g b / none)" but got "color(prophoto-rgb 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 none / 0.4)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / 40%) prophoto-rgb r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(prophoto-rgb 0.7 0.5 0.3 / none)\nExpected: color(from color(prophoto-rgb 0.7 0.5 0.3 / 0.4) prophoto-rgb r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb none none none) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb none none none) prophoto-rgb r g b)" but got "color(prophoto-rgb 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb none none none / none) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb none none none / none) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 none 0.3) prophoto-rgb r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 none 0.3) prophoto-rgb r g b)" but got "color(prophoto-rgb 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(prophoto-rgb 0.7 0.5 0.3 / none) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(prophoto-rgb 0.7 0.5 0.3 / none) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.3 / 0)"
 [FAIL] e.style['color'] = "color(from currentColor prophoto-rgb r g b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in prophoto-rgb, color(prophoto-rgb 0.7 0.5 0.3), color(prophoto-rgb 0.7 0.5 0.3)) prophoto-rgb r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in prophoto-rgb, color(prophoto-rgb 0.7 0.5 0.3), color(prophoto-rgb 0.7 0.5 0.3)) prophoto-rgb r g b / alpha)" but got "color(prophoto-rgb 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b)" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / alpha)" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(display-p    / ) display-p r g b)" but got "color(display-p    / )"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(display-p    / ) display-p r g b / alpha)" but got "color(display-p    / )"
-[FAIL] e.style['color'] = "color(from color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b) display-p3 r g b)" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 0 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 0 0)" but got "color(display-p3 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 0 0 / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 0 0 / 0)" but got "color(display-p3 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 0 g b / alpha)" but got "color(display-p3 0 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 0 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 0 b / alpha)" but got "color(display-p3 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 0 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 0 / alpha)" but got "color(display-p3 0.7 0.5 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 0)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 0)" but got "color(display-p3 0.7 0.5 0.3 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 0 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 0 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r 0 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r 0 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / 0)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 0.2 g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 0.2 g b / alpha)" but got "color(display-p3 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 20% g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 20% g b / alpha)" but got "color(display-p3 0.2 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 0.2 b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 0.2 b / alpha)" but got "color(display-p3 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 20% b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r 20% b / alpha)" but got "color(display-p3 0.7 0.2 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 0.2 / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 0.2 / alpha)" but got "color(display-p3 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 20% / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g 20% / alpha)" but got "color(display-p3 0.7 0.5 0.2)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 0.2)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 0.2)" but got "color(display-p3 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 20%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / 20%)" but got "color(display-p3 0.7 0.5 0.3 / 0.2)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 0.2 g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 0.2 g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 20% g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.2 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 20% g b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r 0.2 b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r 0.2 b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r 20% b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.2 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r 20% b / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g 20% / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.2 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g 20% / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / 20%)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.2)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 2 3 4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 2 3 4)" but got "color(display-p3 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 2 3 4 / 5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 2 3 4 / 5)" but got "color(display-p3 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 -2 -3 -4)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 -2 -3 -4)" but got "color(display-p3 -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 -2 -3 -4 / -5)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 -2 -3 -4 / -5)" but got "color(display-p3 -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 200% 300% 400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 200% 300% 400%)" but got "color(display-p3 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 200% 300% 400% / 500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 200% 300% 400% / 500%)" but got "color(display-p3 2 3 4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 -200% -300% -400%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 -200% -300% -400%)" but got "color(display-p3 -2 -3 -4)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 -200% -300% -400% / -500%)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 -200% -300% -400% / -500%)" but got "color(display-p3 -2 -3 -4 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 g b r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 g b r)" but got "color(display-p3 0.5 0.3 0.7)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 b alpha r / g)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 b alpha r / g)" but got "color(display-p3 0.3 1 0.7 / 0.5)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r r r / r)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r r r / r)" but got "color(display-p3 0.7 0.7 0.7 / 0.7)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 alpha alpha alpha / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 alpha alpha alpha / alpha)" but got "color(display-p3 1 1 1)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 g b r)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.5 0.3 0.7 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 g b r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.5
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 b alpha r / g)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.3 0.4 0.7 / 0.5)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 b alpha r / g).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.3
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r r r / r)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.7 0.7 / 0.7)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r r r / r).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected 0.5 +/- 0.01, expected 0.5 but got 0.7
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 alpha alpha alpha / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.4 0.4 0.4 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 alpha alpha alpha / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 0.7 +/- 0.01, expected 0.7 but got 0.4
-[FAIL] e.style['color'] = "color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b)" but got "color(display-p3 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b / alpha)" but got "color(display-p3 1.7 1.5 1.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 1.7 1.5 1.3 / 140%) display-p3 r g b)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 1.7 1.5 1.3)\nExpected: color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(display-p   ) display-p r g b)" but got "color(display-p   )"
-[FAIL] e.style['color'] = "color(from color(display-p3 1.7 1.5 1.3 / 140%) display-p3 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 1.7 1.5 1.3)\nExpected: color(from color(display-p3 1.7 1.5 1.3) display-p3 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(display-p   ) display-p r g b / alpha)" but got "color(display-p   )"
-[FAIL] e.style['color'] = "color(from color(display-p3 -0.7 -0.5 -0.3) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 -0.7 -0.5 -0.3) display-p3 r g b)" but got "color(display-p3 -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 -0.7 -0.5 -0.3) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 -0.7 -0.5 -0.3) display-p3 r g b / alpha)" but got "color(display-p3 -0.7 -0.5 -0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 -0.7 -0.5 -0.3 / -40%) display-p3 r g b)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(display-p3 -0.7 -0.5 -0.3 / 0) display-p3 r g b).\nError: assert_equals: Color format is correct. expected "color(from color(display-p - - - / ) display-p r g b)" but got "color(display-p - - - / )"
-[FAIL] e.style['color'] = "color(from color(display-p3 -0.7 -0.5 -0.3 / -40%) display-p3 r g b / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 -0.7 -0.5 -0.3 / 0)\nExpected: color(from color(display-p3 -0.7 -0.5 -0.3 / 0) display-p3 r g b / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(display-p - - - / ) display-p r g b / alpha)" but got "color(display-p - - - / )"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 calc(r) calc(g) calc(b))" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 calc(r) calc(g) calc(b))" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 calc(r) calc(g) calc(b) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 calc(r) calc(g) calc(b) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(display-p    / ) display-p calc(r) calc(g) calc(b) / calc(alpha))" but got "color(display-p    / )"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 none none none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 none none none)" but got "color(display-p3 none none none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 none none none / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 none none none / none)" but got "color(display-p3 none none none / none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g none)" but got "color(display-p3 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g none / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g none / alpha)" but got "color(display-p3 0.7 0.5 none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / none)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3) display-p3 r g b / none)" but got "color(display-p3 0.7 0.5 0.3 / none)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 none / 0.4)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / 40%) display-p3 r g b / none)" should set the property value
-  Colors do not match.\nActual:   color(display-p3 0.7 0.5 0.3 / none)\nExpected: color(from color(display-p3 0.7 0.5 0.3 / 0.4) display-p3 r g b / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(display-p3 none none none) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 none none none) display-p3 r g b)" but got "color(display-p3 0 0 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 none none none / none) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 none none none / none) display-p3 r g b / alpha)" but got "color(display-p3 0 0 0 / 0)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 none 0.3) display-p3 r g b)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 none 0.3) display-p3 r g b)" but got "color(display-p3 0.7 0 0.3)"
-[FAIL] e.style['color'] = "color(from color(display-p3 0.7 0.5 0.3 / none) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color(display-p3 0.7 0.5 0.3 / none) display-p3 r g b / alpha)" but got "color(display-p3 0.7 0.5 0.3 / 0)"
 [FAIL] e.style['color'] = "color(from currentColor display-p3 r g b)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in display-p3, color(display-p3 0.7 0.5 0.3), color(display-p3 0.7 0.5 0.3)) display-p3 r g b / alpha)" should set the property value
-  assert_equals: serialization should be canonical expected "color(from color-mix(in display-p3, color(display-p3 0.7 0.5 0.3), color(display-p3 0.7 0.5 0.3)) display-p3 r g b / alpha)" but got "color(display-p3 0.7 0.5 0.3)"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / alpha)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z / alpha)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(from color(xyz 7 -20.5 100) xyz x y z) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(from color(xyz-d  - ) xyz-d x y z) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0 / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.2 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0.2 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0.2).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / 20%)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 20 +/- 0.01, expected 20 but got 0.2
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.2 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0.2 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0.2 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 -20.5 100 7)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 7 7)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected -20.5 +/- 0.01, expected -20.5 but got 7
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 -20.5 100 7 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 7 7)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz calc(x) calc(y) calc(z))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 calc(x) calc(y) calc(z)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d calc(x) calc(y) calc(z))" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz calc(x) calc(y) calc(z) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 calc(x) calc(y) calc(z) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d calc(x) calc(y) calc(z) / calc(alpha))" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz none none none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 none none none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 none none none / none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100) xyz x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / none).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / none)" but got "color(xyz-d  -  / none)"
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / 40%) xyz x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / none)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz none none none) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0)\nExpected: color(from color(xyz-d65 none none none) xyz-d65 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "color(from color(xyz none none none / none) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0 / 0)\nExpected: color(from color(xyz-d65 none none none / none) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 4
-[FAIL] e.style['color'] = "color(from color(xyz 7 none 100) xyz x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100)\nExpected: color(from color(xyz-d65 7 none 100) xyz-d65 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "color(from color(xyz 7 -20.5 100 / none) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100 / none) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 4
 [FAIL] e.style['color'] = "color(from currentColor xyz x y z)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in xyz, color(xyz 0.7 0.5 0.3), color(xyz 0.7 0.5 0.3)) xyz x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.7 0.5 0.3)\nExpected: color(from color-mix(in xyz-d65, color(xyz-d65 0.7 0.5 0.3), color(xyz-d65 0.7 0.5 0.3)) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / alpha)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z / alpha)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100)\nExpected: color(from color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z) xyz-d50 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(from color(xyz-d  - ) xyz-d x y z) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 0 0)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 0 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 0 0 / 0)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 0 0 / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 0)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 0).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 0 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0.2 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0.2 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 0.2)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 0.2).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 20%)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 20 +/- 0.01, expected 20 but got 0.2
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0.2 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0.2 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 0.2 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 -20.5 100 7)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 7 7)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected -20.5 +/- 0.01, expected -20.5 but got 7
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 -20.5 100 7 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 7 7)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 calc(x) calc(y) calc(z))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 calc(x) calc(y) calc(z)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d calc(x) calc(y) calc(z))" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 calc(x) calc(y) calc(z) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 calc(x) calc(y) calc(z) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d calc(x) calc(y) calc(z) / calc(alpha))" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 none none none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 none none none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 none none none / none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / none)\nExpected: color(from color(xyz-d50 7 -20.5 100) xyz-d50 x y z / none).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / none)" but got "color(xyz-d  -  / none)"
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 none / 0.4)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / 40%) xyz-d50 x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / none)\nExpected: color(from color(xyz-d50 7 -20.5 100 / 0.4) xyz-d50 x y z / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 none none none) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 0 0)\nExpected: color(from color(xyz-d50 none none none) xyz-d50 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 none none none / none) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0 0 0 / 0)\nExpected: color(from color(xyz-d50 none none none / none) xyz-d50 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 none 100) xyz-d50 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 0 100)\nExpected: color(from color(xyz-d50 7 none 100) xyz-d50 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d50 7 -20.5 100 / none) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d50 7 -20.5 100 / none) xyz-d50 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 4
 [FAIL] e.style['color'] = "color(from currentColor xyz-d50 x y z)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in xyz-d50, color(xyz-d50 0.7 0.5 0.3), color(xyz-d50 0.7 0.5 0.3)) xyz-d50 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d50 0.7 0.5 0.3)\nExpected: color(from color-mix(in xyz-d50, color(xyz-d50 0.7 0.5 0.3), color(xyz-d50 0.7 0.5 0.3)) xyz-d50 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / alpha)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / alpha).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d x y z / alpha)" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z) xyz-d65 x y z).\nError: assert_equals: Color format is correct. expected "color(from color(from color(xyz-d  - ) xyz-d x y z) xyz-d x y z)" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0 / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 0 0 / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 7 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 0 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 0 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x 0 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x 0 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y 0 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y 0 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z / 0)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / 0).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.2 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0.2 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 0.2).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / )" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 20%)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / 20%).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 3, expected 20 +/- 0.01, expected 20 but got 0.2
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 0.2 y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.2 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 0.2 y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x 0.2 z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0.2 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x 0.2 z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y 0.2 / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 0.2 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y 0.2 / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z / 0.2)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.2)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / 0.2).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 5 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 -20.5 100 7)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 7 7)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 1, expected -20.5 +/- 0.01, expected -20.5 but got 7
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 y z x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 -20.5 100 7 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 y z x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. property 0, expected 7 +/- 0.01, expected 7 but got -20.5
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x x x / x)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 7 7)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x x x / x).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 calc(x) calc(y) calc(z))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 calc(x) calc(y) calc(z)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d calc(x) calc(y) calc(z))" but got "color(xyz-d  - )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 calc(x) calc(y) calc(z) / calc(alpha))" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 calc(x) calc(y) calc(z) / calc(alpha)).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  -  / ) xyz-d calc(x) calc(y) calc(z) / calc(alpha))" but got "color(xyz-d  -  / )"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 none none none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 none none none / none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 none none none / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 2
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / none)\nExpected: color(from color(xyz-d65 7 -20.5 100) xyz-d65 x y z / none).\nError: assert_equals: Color format is correct. expected "color(from color(xyz-d  - ) xyz-d x y z / none)" but got "color(xyz-d  -  / none)"
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y none / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 none / 0.4)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y none / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / 40%) xyz-d65 x y z / none)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / none)\nExpected: color(from color(xyz-d65 7 -20.5 100 / 0.4) xyz-d65 x y z / none).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 4 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 none none none) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0)\nExpected: color(from color(xyz-d65 none none none) xyz-d65 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 none none none / none) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0 0 0 / 0)\nExpected: color(from color(xyz-d65 none none none / none) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 0 got 4
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 none 100) xyz-d65 x y z)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 0 100)\nExpected: color(from color(xyz-d65 7 none 100) xyz-d65 x y z).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 2 got 3
-[FAIL] e.style['color'] = "color(from color(xyz-d65 7 -20.5 100 / none) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 7 -20.5 100 / 0)\nExpected: color(from color(xyz-d65 7 -20.5 100 / none) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 4
 [FAIL] e.style['color'] = "color(from currentColor xyz-d65 x y z)" should set the property value
   assert_not_equals: property should be set got disallowed value ""
-[FAIL] e.style['color'] = "color(from color-mix(in xyz-d65, color(xyz-d65 0.7 0.5 0.3), color(xyz-d65 0.7 0.5 0.3)) xyz-d65 x y z / alpha)" should set the property value
-  Colors do not match.\nActual:   color(xyz-d65 0.7 0.5 0.3)\nExpected: color(from color-mix(in xyz-d65, color(xyz-d65 0.7 0.5 0.3), color(xyz-d65 0.7 0.5 0.3)) xyz-d65 x y z / alpha).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 6 got 3
-[FAIL] e.style['color'] = "rgb(from indianred 255 g b)" should set the property value
-  assert_equals: serialization should be canonical expected "rgb(from indianred 255 g b)" but got "color(srgb 1 0.360784 0.360784)"
 [FAIL] e.style['color'] = "lch(from peru calc(l * 0.8) c h)" should set the property value
-  Colors do not match.\nActual:   lch(49.7972 54.0177 63.6639)\nExpected: lch(from peru calc(0.8 * l) c h).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 1 got 3
+  Colors do not match.\nActual:   lch(from peru calc(l * 0.8) c h)\nExpected: lch(from peru calc(0.8 * l) c h).\nError: assert_equals: Color format is correct. expected "lch(from peru calc( * l) c h)" but got "lch(from peru calc(l * ) c h)"
+[FAIL] e.style['color'] = "color(from rebeccapurple srgb r g b)" should set the property value
+  Colors do not match.\nActual:   color(from rebeccapurple srgb r g b)\nExpected: color(srgb 0.4 0.2 0.6).\nError: assert_array_approx_equals: Numeric parameters are approximately equal. lengths differ, expected 3 got 0
+[FAIL] e.style['color'] = "rgb(from color(srgb 0.4 0.2 0.6) r g b)" should set the property value
+  Colors do not match.\nActual:   rgb(from color(srgb 0.4 0.2 0.6) r g b)\nExpected: color(srgb 0.4 0.2 0.6).\nError: assert_equals: Color format is correct. expected "color(srgb   )" but got "rgb(from color(srgb   ) r g b)"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/expect-no-embedded-resources/README.md b/third_party/blink/web_tests/virtual/expect-no-embedded-resources/README.md
new file mode 100644
index 0000000..193d416
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/expect-no-embedded-resources/README.md
@@ -0,0 +1,4 @@
+# ExpectNoEmbeddedResources
+This suite runs the tests in http/expect-no-embedded-resources/.
+
+See crbug.com/365632977.
diff --git a/third_party/blink/web_tests/virtual/fledge-bidding-and-auction/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_5-8-expected.txt b/third_party/blink/web_tests/virtual/fledge-bidding-and-auction/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_5-8-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-bidding-and-auction/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_5-8-expected.txt
@@ -0,0 +1,3 @@
+This is a testharness.js-based test.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/fledge-bidding-and-auction/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_9-12-expected.txt b/third_party/blink/web_tests/virtual/fledge-bidding-and-auction/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_9-12-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-bidding-and-auction/external/wpt/fledge/tentative/get-interest-group-auction-data.https.window_9-12-expected.txt
@@ -0,0 +1,3 @@
+This is a testharness.js-based test.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/import-assertions-enabled/README.md b/third_party/blink/web_tests/virtual/import-assertions-enabled/README.md
deleted file mode 100644
index 40c4b33..0000000
--- a/third_party/blink/web_tests/virtual/import-assertions-enabled/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Import assertions are being removed in favor of import attributes. The
-flag is changed to disabled by default, and tests are kept as-is, so
-keep running them by explicitly passing the flag. Once we have
-confidence it is web compatible to remove, these tests will be updated
-in https://github.com/web-platform-tests/wpt/pull/46020 and the virtual
-suite will be removed.
diff --git a/third_party/blink/web_tests/virtual/partitioned-popins-disabled/external/wpt/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window-expected.txt b/third_party/blink/web_tests/virtual/partitioned-popins-disabled/external/wpt/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window-expected.txt
new file mode 100644
index 0000000..b4ec3e8
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/partitioned-popins-disabled/external/wpt/partitioned-popins/partitioned-popins.proxy-cross.tentative.sub.https.window-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Verify cross-site Partitioned Popins proxies only have access to postMessage and closed methods.
+  assert_equals: expected "Closed,Then," but got "Closed,Blur,Opener,Length,Then,"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/partitioned-popins-disabled/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window-expected.txt b/third_party/blink/web_tests/virtual/partitioned-popins-disabled/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window-expected.txt
new file mode 100644
index 0000000..0a0299b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/partitioned-popins-disabled/external/wpt/partitioned-popins/partitioned-popins.proxy-same.tentative.https.window-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Verify same-origin Partitioned Popins proxies only have access to postMessage and closed methods.
+  assert_equals: expected "Closed,Then," but got "Closed,Blur,OnBlur,Opener,Length,Name,AnonymousIndex,AnonymousName,CustomMethod,CustomAttributeGet,CustomAttributeSet,Then,"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
index ca5ce1a..4f3c744 100644
--- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -286,6 +286,7 @@
 maskRepeat
 maskSize
 maskType
+masonrySlack
 masonryTemplateTracks
 masonryTrack
 masonryTrackEnd
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
index a34090b7..e3f6e6f1 100644
--- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -258,6 +258,7 @@
     mask-repeat
     mask-size
     mask-type
+    masonry-slack
     masonry-template-tracks
     masonry-track-end
     masonry-track-start
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/column-creates-computed-style.html b/third_party/blink/web_tests/wpt_internal/css/css-overflow/column-creates-computed-style.html
new file mode 100644
index 0000000..8654091
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-overflow/column-creates-computed-style.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: ::column creates computed style</title>
+<link rel="help" href="https://github.com/flackr/carousel/tree/main/fragmentation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#div::column { color: green }
+</style>
+<div id=div></div>
+<script>
+  test(() => {
+  assert_equals(getComputedStyle(div, "::column").color, "rgb(0, 128, 0)");
+  }, "::column creates computed style on its originating element");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/column-updates-computed-style.html b/third_party/blink/web_tests/wpt_internal/css/css-overflow/column-updates-computed-style.html
new file mode 100644
index 0000000..1dcaf49
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-overflow/column-updates-computed-style.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: ::column updates computed style</title>
+<link rel="help" href="https://github.com/flackr/carousel/tree/main/fragmentation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.green-column::column { color: red; }
+.red-column::column { color: green; }
+</style>
+<div id="target"></div>
+<script>
+  test(() => {
+  target.classList.add("green-column");
+  assert_equals(getComputedStyle(target, "::column").color, "rgb(255, 0, 0)");
+  target.classList.remove("green-column");
+  target.classList.add("red-column");
+  assert_equals(getComputedStyle(target, "::column").color, "rgb(0, 128, 0)");
+  }, "::column updates computed style on its originating element");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-ui/webkit-user-modify-all-revert.html b/third_party/blink/web_tests/wpt_internal/css/css-ui/webkit-user-modify-all-revert.html
index 404433d4..f549ee847 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-ui/webkit-user-modify-all-revert.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-ui/webkit-user-modify-all-revert.html
@@ -21,7 +21,10 @@
       selection.removeAllRanges();
       selection.addRange(range);
       document.execCommand("delete");
-      assert_equals(element.firstChild, null, "Text node deleted");
+      if (subtest=="revert")
+        assert_equals(element.innerHTML, "<br>", "Text node replaced with <br> tag");
+      else
+        assert_equals(element.firstChild, null, "Text node deleted");
     }, "all:" + subtest + " does not turn off contenteditable");
   }
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/canvas-fallback-content-is-not-drawn-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/canvas-fallback-content-is-not-drawn-ref.html
new file mode 100644
index 0000000..24d450ca
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/canvas-fallback-content-is-not-drawn-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Canvas fallback content is not drawn</title>
+
+<body>
+  <canvas>
+  </canvas>
+</body>
+<!DOCTYPE html>
+<html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/canvas-fallback-content-is-not-drawn.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/canvas-fallback-content-is-not-drawn.html
new file mode 100644
index 0000000..07c7ab5
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/canvas-fallback-content-is-not-drawn.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Canvas fallback content is not drawn to the screen</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="canvas-fallback-content-is-not-drawn-ref.html">
+  <style>
+    #fallback {
+      /*
+        will-change: transform creates a stacking context for the element
+        so it might be rendered if UAs aren't careful
+      */
+      will-change: transform;
+      background-color: pink;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div id="fallback">You should not see this fallback content</div>
+  </canvas>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/layout-canvas-fallback-content.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/layout-canvas-fallback-content.html
new file mode 100644
index 0000000..671261a
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/layout-canvas-fallback-content.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Layout Fallback Content for canvas2D</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <meta name="assert" content="In order for canvas.placeElement() to work, fallback content must be laid out." />
+  <style>
+    #fallback {
+      will-change: transform;
+      width: 100px;
+      height: 50px;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div id="fallback"></div>
+  </canvas>
+  <p>
+    The empty canvas above contains a div with explicit height and width.
+    This test verifies that that div is laid out.
+  </p>
+</body>
+
+<script>
+  const ctx = document.querySelector("canvas").getContext("2d");
+
+  // placeElement() triggers some layout, verify that it works after the page
+  // has already been laid out.
+  setTimeout(() => {
+    const fallbackElement = document.getElementById("fallback");
+    // This call triggers the layout for the fallback content.
+    ctx.placeElement(fallbackElement, 0, 0);
+
+    const rect = fallbackElement.getBoundingClientRect();
+    test(function (t) {
+      assert_equals(100, rect.width);
+      assert_equals(50, rect.height);
+    }, "Verify that the dimensions of the fallback div are correct.");
+  }, 0);
+
+</script>
+
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-100-percent-width-height.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-100-percent-width-height.html
new file mode 100644
index 0000000..208ea9a
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-100-percent-width-height.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement() fills parent container</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <meta name="assert" content="Canvas fallback content fills the canvas with width/height = 100%" />
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+      width: 100%;
+      height: 100%;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div class="fallback" id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+  const canvas = document.querySelector("canvas");
+  const ctx = canvas.getContext("2d");
+  ctx.placeElement(document.getElementById("fallback"), 0, 0);
+
+  const rect = document.getElementById("fallback").getBoundingClientRect();
+  test(function (t) {
+    assert_equals(canvas.width, rect.width);
+    assert_equals(canvas.height, rect.height);
+  }, "Verify that the dimensions of the fallback div are correct.");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-blockify-children.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-blockify-children.html
new file mode 100644
index 0000000..6138a083
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-blockify-children.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Canvas fallback content blockifies children</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <meta name="assert" content="Canvas fallback content blockifies children." />
+</head>
+
+<style>
+  #fallback {
+    will-change: transform;
+  }
+</style>
+
+<body>
+  <canvas>
+    Text outside of span <span id="fallback">blockified text.</span>
+  </canvas>
+  <div id="ref" style="width: fit-content;">blockified text.</div>
+</body>
+
+<script>
+  const ctx = document.querySelector("canvas").getContext("2d");
+
+  const fallbackElement = document.getElementById("fallback");
+  // This call triggers the layout for the fallback content.
+  ctx.placeElement(fallbackElement, 0, 0);
+  const rect = fallbackElement.getBoundingClientRect();
+
+  const referenceDiv = document.getElementById("ref");
+  const refRect = referenceDiv.getBoundingClientRect();
+  test(function (t) {
+    assert_equals(rect.width, refRect.width);
+    assert_equals(rect.height, refRect.height);
+  }, `Verify that the dimensions of the span are correct.
+  Without blockifying the span the layout engine will crash.`);
+
+</script>
+
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-border-and-padding-box-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-border-and-padding-box-ref.html
new file mode 100644
index 0000000..db53b3d
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-border-and-padding-box-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+<style>
+  .fallback {
+    background-color: green;
+    width: 100px;
+    height: 50px;
+    padding: 10px;
+    border: 5px dotted blue;
+  }
+</style>
+
+<body>
+  <div class="fallback" id="fallback">Placed Element</div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-border-and-padding-box.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-border-and-padding-box.html
new file mode 100644
index 0000000..a094748a
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-border-and-padding-box.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement() is placed properly relative to padding and border</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-border-and-padding-box-ref.html">
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+      width: 100px;
+      height: 50px;
+      padding: 10px;
+      border: 5px dotted blue;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div class="fallback" id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+    const ctx = document.querySelector("canvas").getContext("2d");
+    ctx.placeElement(document.getElementById("fallback"), 0, 0);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-draw-atomically-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-draw-atomically-ref.html
new file mode 100644
index 0000000..915c015
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-draw-atomically-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+
+<body>
+  <div id="fallback" style="width:fit-content;">
+    <div style="background:lime; isolation: isolate;">Test</div>
+    <div style="background:orange; margin-top: -15px; isolation: isolate;">Overlap</div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-draw-atomically.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-draw-atomically.html
new file mode 100644
index 0000000..5f3c3be7
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-draw-atomically.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement()s are drawn atomically</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-draw-atomically-ref.html">
+</head>
+
+<body>
+  <canvas>
+    <div id="fallback" style="will-change: transform;">
+      <div style="background:lime;">Test</div>
+      <div style="background:orange; margin-top: -15px;">Overlap</div>
+    </div>
+  </canvas>
+</body>
+
+<script>
+  // The two sub-elements should be drawn with proper overlapping.
+  const ctx = document.querySelector("canvas").getContext("2d");
+  ctx.placeElement(document.getElementById("fallback"), 0, 0);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-explicit-width-height-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-explicit-width-height-ref.html
new file mode 100644
index 0000000..97d3693
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-explicit-width-height-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+<style>
+  .fallback {
+    background-color: green;
+    width: 150px;
+    height: 100px;
+  }
+</style>
+
+<body>
+  <div class="fallback"">Placed Element</div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-explicit-width-height.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-explicit-width-height.html
new file mode 100644
index 0000000..d375743d
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-explicit-width-height.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement() with explicit width and height</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-explicit-width-height-ref.html">
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+      width: 150px;
+      height: 100px;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div class="fallback" id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+  const ctx = document.querySelector("canvas").getContext("2d");
+  ctx.placeElement(document.getElementById("fallback"), 0, 0);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-float-left.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-float-left.html
new file mode 100644
index 0000000..6bf10ab
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-float-left.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement()</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-ref.html">
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+      /* float: left should not change placed element positon */
+      float: left;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas style="position: relative; left: -20px; top: -10px;">
+    <div class="fallback" id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+  const canvas = document.querySelector("canvas");
+  const ctx = canvas.getContext("2d");
+  const fallbackElement = document.getElementById("fallback");
+  ctx.placeElement(fallbackElement, 20, 10);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-position-absolute.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-position-absolute.html
new file mode 100644
index 0000000..15723f8
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-position-absolute.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement() with position: absolute</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-ref.html">
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+      /* absolute position should not move the placed element */
+      position: absolute;
+      left: 50px;
+      top: 30px;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas style="position: relative; left: -20px; top: -10px">
+    <div class="fallback" id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+  const canvas = document.querySelector("canvas");
+  const ctx = canvas.getContext("2d");
+  const fallbackElement = document.getElementById("fallback");
+  ctx.placeElement(fallbackElement, 20, 10);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-ref.html
new file mode 100644
index 0000000..d4bc0499
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+<style>
+  .fallback {
+    background-color: green;
+    width: fit-content;
+  }
+</style>
+
+<body>
+  <div class="fallback"">Placed Element</div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-transform-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-transform-ref.html
new file mode 100644
index 0000000..33afebe
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-transform-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+<style>
+  #fallback {
+    background-color: green;
+    width: 100px;
+    height: 50px;
+    transform: translate(30px, 45px) rotate(60deg) scale(0.5, 1.5);
+  }
+</style>
+
+<body>
+  <div id="fallback">Placed Element</div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-transform.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-transform.html
new file mode 100644
index 0000000..b8611e5
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-transform.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement() respects context transform</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-transform-ref.html">
+  <style>
+    #fallback {
+      will-change: transform;
+      background-color: green;
+      width: 100px;
+      height: 50px;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+  const canvas = document.querySelector("canvas");
+  const ctx = canvas.getContext("2d");
+
+  // The extra translation by w/2 and h/2 is so that the rotation and scaling
+  // happen at the center of the fallback div, which is how it works for css.
+  // This is then undone with the final translation.
+  ctx.translate(50, 25);
+
+  // This matches exactly the transformation in the ref.
+  ctx.translate(30, 45);
+  ctx.rotate(Math.PI / 3);
+  ctx.scale(0.5, 1.5);
+
+  // Undo the first translation above.
+  ctx.translate(-50, -25);
+
+  const fallbackElement = document.getElementById("fallback");
+  ctx.placeElement(fallbackElement, 0, 0);
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-width-auto.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-width-auto.html
new file mode 100644
index 0000000..a343d7e3
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-width-auto.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement()</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-ref.html">
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+      width: auto;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div class="fallback" id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+  const ctx = document.querySelector("canvas").getContext("2d");
+  ctx.placeElement(document.getElementById("fallback"), 0, 0);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-writingMode-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-writingMode-ref.html
new file mode 100644
index 0000000..14505d7
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-writingMode-ref.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+<style>
+  .container {
+    width: 300px;
+    height: 150px;
+  }
+
+  .fallback {
+    background-color: green;
+    width: fit-content;
+  }
+</style>
+
+<body>
+</body>
+
+<script>
+  const WRITING_MODES = [
+    "horizontal-tb",
+    "vertical-lr",
+    "vertical-rl",
+    "sideways-lr",
+    "sideways-rl"
+  ];
+  WRITING_MODES.forEach((mode, index) => {
+    const container = document.createElement("div")
+    container.classList.add("container");
+
+    const element = document.createElement("div");
+    element.classList.add("fallback");
+    element.style.writingMode = mode;
+    element.innerText = mode;
+
+    container.appendChild(element);
+    document.body.appendChild(container);
+  });
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-writingMode.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-writingMode.html
new file mode 100644
index 0000000..47451f6
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-writingMode.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement() with different writing modes</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-writingMode-ref.html">
+  <meta name="fuzzy" content="maxDifference=0-50; totalPixels=0-14000">
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+    }
+    canvas {
+      display: block;
+    }
+  </style>
+</head>
+
+<body>
+</body>
+
+<script>
+  const WRITING_MODES = [
+    "horizontal-tb",
+    "vertical-lr",
+    "vertical-rl",
+    "sideways-lr",
+    "sideways-rl"
+  ];
+  WRITING_MODES.forEach((mode, index) => {
+    const canvas = document.createElement("canvas");
+    document.body.appendChild(canvas);
+    canvas.style.writingMode = mode;
+    const ctx = canvas.getContext("2d");
+
+    const element = document.createElement("div");
+    element.classList.add("fallback");
+    element.innerText = mode;
+
+    canvas.appendChild(element);
+    ctx.placeElement(element, 0, 0);
+  });
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-x-y-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-x-y-ref.html
new file mode 100644
index 0000000..55bc049
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-x-y-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+<style>
+  .fallback {
+    background-color: green;
+    width: 100px;
+    height: 50px;
+    transform: matrix(1, 0, 0, 1, 100, 50);
+  }
+</style>
+
+<body>
+  <div class="fallback" id="fallback">Placed Element</div>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-x-y.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-x-y.html
new file mode 100644
index 0000000..225f546
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement-x-y.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement()s x and y parameters are working</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-x-y-ref.html">
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+      width: 100px;
+      height: 50px;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div class="fallback" id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+  const canvas = document.querySelector("canvas");
+  const ctx = canvas.getContext("2d");
+  const fallbackElement = document.getElementById("fallback");
+  ctx.placeElement(fallbackElement, 100, 50);
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement.html b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement.html
new file mode 100644
index 0000000..f67ab76
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/placeElement/placeElement.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+
+<head>
+  <title>CanvasRenderingContext2D.PlaceElement()</title>
+  <link rel="author" title="Aaron Krajeski" href="mailto:aaronhk@chromium.org" />
+  <link rel="help" href="https://github.com/Igalia/explainers/blob/main/canvas-formatted-text/html-in-canvas.md" />
+  <link rel="match" href="placeElement-ref.html">
+  <style>
+    .fallback {
+      will-change: transform;
+      background-color: green;
+    }
+  </style>
+</head>
+
+<body>
+  <canvas>
+    <div class="fallback" id="fallback">Placed Element</div>
+  </canvas>
+</body>
+
+<script>
+  const ctx = document.querySelector("canvas").getContext("2d");
+  ctx.placeElement(document.getElementById("fallback"), 0, 0);
+</script>
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index 40ec347..e724ef0 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit 40ec347196a939bd4fd1f801df896b2f4e2205dc
+Subproject commit e724ef02089bf2bb494203231fc5cb62acc2fad6
diff --git a/third_party/chromite b/third_party/chromite
index c65df07..c84bf5c 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit c65df07901b4f506da77443659bcf0319b70e8cd
+Subproject commit c84bf5c21fa3c0340441768c1feeacc96cf46497
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index 2d53ec2..5a75b26a 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit 2d53ec23e6a27cba13b7695c0d73efe5dcaec1e7
+Subproject commit 5a75b26ac7173f7a9b010b02a0cbbc60ef5c159c
diff --git a/third_party/closure_compiler/externs/accessibility_private.js b/third_party/closure_compiler/externs/accessibility_private.js
index 3d0be91..9e4e4d3 100644
--- a/third_party/closure_compiler/externs/accessibility_private.js
+++ b/third_party/closure_compiler/externs/accessibility_private.js
@@ -609,8 +609,8 @@
  * @param {!chrome.accessibilityPrivate.SyntheticKeyboardEvent} keyEvent The
  *     event to send.
  * @param {boolean=} useRewriters If true, uses rewriters for the key event;
- *     only allowed if used from Dictation. Otherwise indicates that rewriters
- *     should be skipped.
+ *     only allowed if used from Dictation or FaceGaze. Otherwise indicates that
+ *     rewriters should be skipped.
  */
 chrome.accessibilityPrivate.sendSyntheticKeyEvent = function(keyEvent, useRewriters) {};
 
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 1ad5b6c..17226d7 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 1ad5b6c0df87d570420c9f833c0c024fa863853b
+Subproject commit 17226d7965e188c68162b4765ffaa24ec8883f52
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index d28a0b7..3a82558 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit d28a0b79790f235c4124e148509fff1a92bd832b
+Subproject commit 3a82558ffc48810616f624261d343591799d5730
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 535c69c..4b11dfe 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 535c69c69e1bcbcfc8f47dd93d74374abdbb80a1
+Subproject commit 4b11dfea9a3853a1da464c2e527408c7cd6091de
diff --git a/third_party/motemplate/.gitignore b/third_party/motemplate/.gitignore
deleted file mode 100644
index 0d20b648..0000000
--- a/third_party/motemplate/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.pyc
diff --git a/third_party/motemplate/DIR_METADATA b/third_party/motemplate/DIR_METADATA
deleted file mode 100644
index 45f7798a6..0000000
--- a/third_party/motemplate/DIR_METADATA
+++ /dev/null
@@ -1,6 +0,0 @@
-monorail: {
-  component: "Internals"
-}
-buganizer_public: {
-  component_id: 1456292
-}
diff --git a/third_party/motemplate/LICENSE b/third_party/motemplate/LICENSE
deleted file mode 100644
index d645695..0000000
--- a/third_party/motemplate/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   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.
diff --git a/third_party/motemplate/OWNERS b/third_party/motemplate/OWNERS
deleted file mode 100644
index e06924a..0000000
--- a/third_party/motemplate/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-rockot@google.com
diff --git a/third_party/motemplate/README.chromium b/third_party/motemplate/README.chromium
deleted file mode 100644
index 4e6ef39..0000000
--- a/third_party/motemplate/README.chromium
+++ /dev/null
@@ -1,13 +0,0 @@
-Name: Motemplate, cross-platform data binding templates.
-Short Name: motemplate
-URL: https://github.com/kalman/templates
-Version: 0
-Date: August 22, 2014
-Revision: commit 76237c8ece0272dc02d104eef24b66365b4feaf3
-License: Apache 2.0
-License File: LICENSE
-Security Critical: no
-Shipped: no
-
-Description:
-A data binding template language for Python inspired by ctemplate.
diff --git a/third_party/motemplate/motemplate.py b/third_party/motemplate/motemplate.py
deleted file mode 100644
index 397228ba..0000000
--- a/third_party/motemplate/motemplate.py
+++ /dev/null
@@ -1,1155 +0,0 @@
-# Copyright 2012 Benjamin Kalman
-#
-# 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.
-
-# TODO: Escaping control characters somehow. e.g. \{{, \{{-.
-
-import json
-import re
-
-'''Motemplate templates are data binding templates more-than-loosely inspired by
-ctemplate. Use like:
-
-  from motemplate import Motemplate
-
-  template = Motemplate('hello {{#foo bar/}} world')
-  input = {
-    'foo': [
-      { 'bar': 1 },
-      { 'bar': 2 },
-      { 'bar': 3 }
-    ]
-  }
-  print(template.render(input).text)
-
-Motemplate will use get() on contexts to return values, so to create custom
-getters (for example, something that populates values lazily from keys), just
-provide an object with a get() method.
-
-  class CustomContext(object):
-    def get(self, key):
-      return 10
-  print(Motemplate('hello {{world}}').render(CustomContext()).text)
-
-will print 'hello 10'.
-'''
-
-class ParseException(Exception):
-  '''The exception thrown while parsing a template.
-  '''
-  def __init__(self, error):
-    Exception.__init__(self, error)
-
-class RenderResult(object):
-  '''The result of a render operation.
-  '''
-  def __init__(self, text, errors):
-    self.text = text;
-    self.errors = errors
-
-  def __repr__(self):
-    return '%s(text=%s, errors=%s)' % (type(self).__name__,
-                                       self.text,
-                                       self.errors)
-
-  def __str__(self):
-    return repr(self)
-
-class _StringBuilder(object):
-  '''Efficiently builds strings.
-  '''
-  def __init__(self):
-    self._buf = []
-
-  def __len__(self):
-    self._Collapse()
-    return len(self._buf[0])
-
-  def Append(self, string):
-    if not isinstance(string, basestring):
-      string = str(string)
-    self._buf.append(string)
-
-  def ToString(self):
-    self._Collapse()
-    return self._buf[0]
-
-  def _Collapse(self):
-    self._buf = [u''.join(self._buf)]
-
-  def __repr__(self):
-    return self.ToString()
-
-  def __str__(self):
-    return repr(self)
-
-class _Contexts(object):
-  '''Tracks a stack of context objects, providing efficient key/value retrieval.
-  '''
-  class _Node(object):
-    '''A node within the stack. Wraps a real context and maintains the key/value
-    pairs seen so far.
-    '''
-    def __init__(self, value):
-      self._value = value
-      self._value_has_get = hasattr(value, 'get')
-      self._found = {}
-
-    def GetKeys(self):
-      '''Returns the list of keys that |_value| contains.
-      '''
-      return self._found.keys()
-
-    def Get(self, key):
-      '''Returns the value for |key|, or None if not found (including if
-      |_value| doesn't support key retrieval).
-      '''
-      if not self._value_has_get:
-        return None
-      value = self._found.get(key)
-      if value is not None:
-        return value
-      value = self._value.get(key)
-      if value is not None:
-        self._found[key] = value
-      return value
-
-    def __repr__(self):
-      return 'Node(value=%s, found=%s)' % (self._value, self._found)
-
-    def __str__(self):
-      return repr(self)
-
-  def __init__(self, globals_):
-    '''Initializes with the initial global contexts, listed in order from most
-    to least important.
-    '''
-    self._nodes = map(_Contexts._Node, globals_)
-    self._first_local = len(self._nodes)
-    self._value_info = {}
-
-  def CreateFromGlobals(self):
-    new = _Contexts([])
-    new._nodes = self._nodes[:self._first_local]
-    new._first_local = self._first_local
-    return new
-
-  def Push(self, context):
-    self._nodes.append(_Contexts._Node(context))
-
-  def Pop(self):
-    node = self._nodes.pop()
-    assert len(self._nodes) >= self._first_local
-    for found_key in node.GetKeys():
-      # [0] is the stack of nodes that |found_key| has been found in.
-      self._value_info[found_key][0].pop()
-
-  def FirstLocal(self):
-    if len(self._nodes) == self._first_local:
-      return None
-    return self._nodes[-1]._value
-
-  def Resolve(self, path):
-    # This method is only efficient at finding |key|; if |tail| has a value (and
-    # |key| evaluates to an indexable value) we'll need to descend into that.
-    key, tail = path.split('.', 1) if '.' in path else (path, None)
-    found = self._FindNodeValue(key)
-    if tail is None:
-      return found
-    for part in tail.split('.'):
-      if not hasattr(found, 'get'):
-        return None
-      found = found.get(part)
-    return found
-
-  def Scope(self, context, fn, *args):
-    self.Push(context)
-    try:
-      return fn(*args)
-    finally:
-      self.Pop()
-
-  def _FindNodeValue(self, key):
-    # |found_node_list| will be all the nodes that |key| has been found in.
-    # |checked_node_set| are those that have been checked.
-    info = self._value_info.get(key)
-    if info is None:
-      info = ([], set())
-      self._value_info[key] = info
-    found_node_list, checked_node_set = info
-
-    # Check all the nodes not yet checked for |key|.
-    newly_found = []
-    for node in reversed(self._nodes):
-      if node in checked_node_set:
-        break
-      value = node.Get(key)
-      if value is not None:
-        newly_found.append(node)
-      checked_node_set.add(node)
-
-    # The nodes will have been found in reverse stack order. After extending
-    # the found nodes, the freshest value will be at the tip of the stack.
-    found_node_list.extend(reversed(newly_found))
-    if not found_node_list:
-      return None
-
-    return found_node_list[-1]._value.get(key)
-
-class _Stack(object):
-  class Entry(object):
-    def __init__(self, name, id_):
-      self.name = name
-      self.id_ = id_
-
-  def __init__(self, entries=[]):
-    self.entries = entries
-
-  def Descend(self, name, id_):
-    descended = list(self.entries)
-    descended.append(_Stack.Entry(name, id_))
-    return _Stack(entries=descended)
-
-class _InternalContext(object):
-  def __init__(self):
-    self._render_state = None
-
-  def SetRenderState(self, render_state):
-    self._render_state = render_state
-
-  def get(self, key):
-    if key == 'errors':
-      errors = self._render_state._errors
-      return '\n'.join(errors) if errors else None
-    return None
-
-class _RenderState(object):
-  '''The state of a render call.
-  '''
-  def __init__(self, name, contexts, _stack=_Stack()):
-    self.text = _StringBuilder()
-    self.contexts = contexts
-    self._name = name
-    self._errors = []
-    self._stack = _stack
-
-  def AddResolutionError(self, id_, description=None):
-    message = id_.CreateResolutionErrorMessage(self._name, stack=self._stack)
-    if description is not None:
-      message = '%s (%s)' % (message, description)
-    self._errors.append(message)
-
-  def Copy(self):
-    return _RenderState(
-        self._name, self.contexts, _stack=self._stack)
-
-  def ForkPartial(self, custom_name, id_):
-    name = custom_name or id_.name
-    return _RenderState(name,
-                        self.contexts.CreateFromGlobals(),
-                        _stack=self._stack.Descend(name, id_))
-
-  def Merge(self, render_state, text_transform=None):
-    self._errors.extend(render_state._errors)
-    text = render_state.text.ToString()
-    if text_transform is not None:
-      text = text_transform(text)
-    self.text.Append(text)
-
-  def GetResult(self):
-    return RenderResult(self.text.ToString(), self._errors);
-
-class _Identifier(object):
-  '''An identifier of the form 'foo', 'foo.bar.baz', 'foo-bar.baz', etc.
-  '''
-  _VALID_ID_MATCHER = re.compile(r'^[a-zA-Z0-9@_/-]+$')
-
-  def __init__(self, name, line, column):
-    self.name = name
-    self.line = line
-    self.column = column
-    if name == '':
-      raise ParseException('Empty identifier %s' % self.GetDescription())
-    for part in name.split('.'):
-      if not _Identifier._VALID_ID_MATCHER.match(part):
-        raise ParseException('Invalid identifier %s' % self.GetDescription())
-
-  def GetDescription(self):
-    return '\'%s\' at line %s column %s' % (self.name, self.line, self.column)
-
-  def CreateResolutionErrorMessage(self, name, stack=None):
-    message = _StringBuilder()
-    message.Append('Failed to resolve %s in %s\n' % (self.GetDescription(),
-                                                     name))
-    if stack is not None:
-      for entry in reversed(stack.entries):
-        message.Append('  included as %s in %s\n' % (entry.id_.GetDescription(),
-                                                     entry.name))
-    return message.ToString().strip()
-
-  def __repr__(self):
-    return self.name
-
-  def __str__(self):
-    return repr(self)
-
-class _Node(object): pass
-
-class _LeafNode(_Node):
-  def __init__(self, start_line, end_line):
-    self._start_line = start_line
-    self._end_line = end_line
-
-  def StartsWithNewLine(self):
-    return False
-
-  def TrimStartingNewLine(self):
-    pass
-
-  def TrimEndingSpaces(self):
-    return 0
-
-  def TrimEndingNewLine(self):
-    pass
-
-  def EndsWithEmptyLine(self):
-    return False
-
-  def GetStartLine(self):
-    return self._start_line
-
-  def GetEndLine(self):
-    return self._end_line
-
-  def __str__(self):
-    return repr(self)
-
-class _DecoratorNode(_Node):
-  def __init__(self, content):
-    self._content = content
-
-  def StartsWithNewLine(self):
-    return self._content.StartsWithNewLine()
-
-  def TrimStartingNewLine(self):
-    self._content.TrimStartingNewLine()
-
-  def TrimEndingSpaces(self):
-    return self._content.TrimEndingSpaces()
-
-  def TrimEndingNewLine(self):
-    self._content.TrimEndingNewLine()
-
-  def EndsWithEmptyLine(self):
-    return self._content.EndsWithEmptyLine()
-
-  def GetStartLine(self):
-    return self._content.GetStartLine()
-
-  def GetEndLine(self):
-    return self._content.GetEndLine()
-
-  def __repr__(self):
-    return str(self._content)
-
-  def __str__(self):
-    return repr(self)
-
-class _InlineNode(_DecoratorNode):
-  def __init__(self, content):
-    _DecoratorNode.__init__(self, content)
-
-  def Render(self, render_state):
-    content_render_state = render_state.Copy()
-    self._content.Render(content_render_state)
-    render_state.Merge(content_render_state,
-                       text_transform=lambda text: text.replace('\n', ''))
-
-class _IndentedNode(_DecoratorNode):
-  def __init__(self, content, indentation):
-    _DecoratorNode.__init__(self, content)
-    self._indent_str = ' ' * indentation
-
-  def Render(self, render_state):
-    if isinstance(self._content, _CommentNode):
-      return
-    def inlinify(text):
-      if len(text) == 0:  # avoid rendering a blank line
-        return ''
-      buf = _StringBuilder()
-      buf.Append(self._indent_str)
-      buf.Append(text.replace('\n', '\n%s' % self._indent_str))
-      if not text.endswith('\n'):  # partials will often already end in a \n
-        buf.Append('\n')
-      return buf.ToString()
-    content_render_state = render_state.Copy()
-    self._content.Render(content_render_state)
-    render_state.Merge(content_render_state, text_transform=inlinify)
-
-class _BlockNode(_DecoratorNode):
-  def __init__(self, content):
-    _DecoratorNode.__init__(self, content)
-    content.TrimStartingNewLine()
-    content.TrimEndingSpaces()
-
-  def Render(self, render_state):
-    self._content.Render(render_state)
-
-class _NodeCollection(_Node):
-  def __init__(self, nodes):
-    assert nodes
-    self._nodes = nodes
-
-  def Render(self, render_state):
-    for node in self._nodes:
-      node.Render(render_state)
-
-  def StartsWithNewLine(self):
-    return self._nodes[0].StartsWithNewLine()
-
-  def TrimStartingNewLine(self):
-    self._nodes[0].TrimStartingNewLine()
-
-  def TrimEndingSpaces(self):
-    return self._nodes[-1].TrimEndingSpaces()
-
-  def TrimEndingNewLine(self):
-    self._nodes[-1].TrimEndingNewLine()
-
-  def EndsWithEmptyLine(self):
-    return self._nodes[-1].EndsWithEmptyLine()
-
-  def GetStartLine(self):
-    return self._nodes[0].GetStartLine()
-
-  def GetEndLine(self):
-    return self._nodes[-1].GetEndLine()
-
-  def __repr__(self):
-    return ''.join(str(node) for node in self._nodes)
-
-class _StringNode(_Node):
-  '''Just a string.
-  '''
-  def __init__(self, string, start_line, end_line):
-    self._string = string
-    self._start_line = start_line
-    self._end_line = end_line
-
-  def Render(self, render_state):
-    render_state.text.Append(self._string)
-
-  def StartsWithNewLine(self):
-    return self._string.startswith('\n')
-
-  def TrimStartingNewLine(self):
-    if self.StartsWithNewLine():
-      self._string = self._string[1:]
-
-  def TrimEndingSpaces(self):
-    original_length = len(self._string)
-    self._string = self._string[:self._LastIndexOfSpaces()]
-    return original_length - len(self._string)
-
-  def TrimEndingNewLine(self):
-    if self._string.endswith('\n'):
-      self._string = self._string[:len(self._string) - 1]
-
-  def EndsWithEmptyLine(self):
-    index = self._LastIndexOfSpaces()
-    return index == 0 or self._string[index - 1] == '\n'
-
-  def _LastIndexOfSpaces(self):
-    index = len(self._string)
-    while index > 0 and self._string[index - 1] == ' ':
-      index -= 1
-    return index
-
-  def GetStartLine(self):
-    return self._start_line
-
-  def GetEndLine(self):
-    return self._end_line
-
-  def __repr__(self):
-    return self._string
-
-class _EscapedVariableNode(_LeafNode):
-  '''{{foo}}
-  '''
-  def __init__(self, id_):
-    _LeafNode.__init__(self, id_.line, id_.line)
-    self._id = id_
-
-  def Render(self, render_state):
-    value = render_state.contexts.Resolve(self._id.name)
-    if value is None:
-      render_state.AddResolutionError(self._id)
-      return
-    string = value if isinstance(value, basestring) else str(value)
-    render_state.text.Append(string.replace('&', '&amp;')
-                                   .replace('<', '&lt;')
-                                   .replace('>', '&gt;'))
-
-  def __repr__(self):
-    return '{{%s}}' % self._id
-
-class _UnescapedVariableNode(_LeafNode):
-  '''{{{foo}}}
-  '''
-  def __init__(self, id_):
-    _LeafNode.__init__(self, id_.line, id_.line)
-    self._id = id_
-
-  def Render(self, render_state):
-    value = render_state.contexts.Resolve(self._id.name)
-    if value is None:
-      render_state.AddResolutionError(self._id)
-      return
-    string = value if isinstance(value, basestring) else str(value)
-    render_state.text.Append(string)
-
-  def __repr__(self):
-    return '{{{%s}}}' % self._id
-
-class _CommentNode(_LeafNode):
-  '''{{- This is a comment -}}
-  An empty placeholder node for correct indented rendering behaviour.
-  '''
-  def __init__(self, start_line, end_line):
-    _LeafNode.__init__(self, start_line, end_line)
-
-  def Render(self, render_state):
-    pass
-
-  def __repr__(self):
-    return '<comment>'
-
-class _SectionNode(_DecoratorNode):
-  '''{{#var:foo}} ... {{/foo}}
-  '''
-  def __init__(self, bind_to, id_, content):
-    _DecoratorNode.__init__(self, content)
-    self._bind_to = bind_to
-    self._id = id_
-
-  def Render(self, render_state):
-    value = render_state.contexts.Resolve(self._id.name)
-    if isinstance(value, list):
-      for item in value:
-        if self._bind_to is not None:
-          render_state.contexts.Scope({self._bind_to.name: item},
-                                      self._content.Render, render_state)
-        else:
-          self._content.Render(render_state)
-    elif hasattr(value, 'get'):
-      if self._bind_to is not None:
-        render_state.contexts.Scope({self._bind_to.name: value},
-                                    self._content.Render, render_state)
-      else:
-        render_state.contexts.Scope(value, self._content.Render, render_state)
-    else:
-      render_state.AddResolutionError(self._id)
-
-  def __repr__(self):
-    return '{{#%s}}%s{{/%s}}' % (
-        self._id, _DecoratorNode.__repr__(self), self._id)
-
-class _VertedSectionNode(_DecoratorNode):
-  '''{{?var:foo}} ... {{/foo}}
-  '''
-  def __init__(self, bind_to, id_, content):
-    _DecoratorNode.__init__(self, content)
-    self._bind_to = bind_to
-    self._id = id_
-
-  def Render(self, render_state):
-    value = render_state.contexts.Resolve(self._id.name)
-    if _VertedSectionNode.ShouldRender(value):
-      if self._bind_to is not None:
-        render_state.contexts.Scope({self._bind_to.name: value},
-                                    self._content.Render, render_state)
-      else:
-        self._content.Render(render_state)
-
-  def __repr__(self):
-    return '{{?%s}}%s{{/%s}}' % (
-        self._id, _DecoratorNode.__repr__(self), self._id)
-
-  @staticmethod
-  def ShouldRender(value):
-    if value is None:
-      return False
-    if isinstance(value, bool):
-      return value
-    if isinstance(value, list):
-      return len(value) > 0
-    return True
-
-class _InvertedSectionNode(_DecoratorNode):
-  '''{{^foo}} ... {{/foo}}
-  '''
-  def __init__(self, bind_to, id_, content):
-    _DecoratorNode.__init__(self, content)
-    if bind_to is not None:
-      raise ParseException('{{^%s:%s}} does not support variable binding'
-                           % (bind_to, id_))
-    self._id = id_
-
-  def Render(self, render_state):
-    value = render_state.contexts.Resolve(self._id.name)
-    if not _VertedSectionNode.ShouldRender(value):
-      self._content.Render(render_state)
-
-  def __repr__(self):
-    return '{{^%s}}%s{{/%s}}' % (
-        self._id, _DecoratorNode.__repr__(self), self._id)
-
-class _AssertionNode(_LeafNode):
-  '''{{!foo Some comment about foo}}
-  '''
-  def __init__(self, id_, description):
-    _LeafNode.__init__(self, id_.line, id_.line)
-    self._id = id_
-    self._description = description
-
-  def Render(self, render_state):
-    if render_state.contexts.Resolve(self._id.name) is None:
-      render_state.AddResolutionError(self._id, description=self._description)
-
-  def __repr__(self):
-    return '{{!%s %s}}' % (self._id, self._description)
-
-class _JsonNode(_LeafNode):
-  '''{{*foo}}
-  '''
-  def __init__(self, id_):
-    _LeafNode.__init__(self, id_.line, id_.line)
-    self._id = id_
-
-  def Render(self, render_state):
-    value = render_state.contexts.Resolve(self._id.name)
-    if value is None:
-      render_state.AddResolutionError(self._id)
-      return
-    render_state.text.Append(json.dumps(value, separators=(',',':')))
-
-  def __repr__(self):
-    return '{{*%s}}' % self._id
-
-class _PartialNodeWithArguments(_DecoratorNode):
-  def __init__(self, partial, args):
-    if isinstance(partial, Motemplate):
-      # Preserve any get() method that the caller has added.
-      if hasattr(partial, 'get'):
-        self.get = partial.get
-      partial = partial._top_node
-    _DecoratorNode.__init__(self, partial)
-    self._partial = partial
-    self._args = args
-
-  def Render(self, render_state):
-    render_state.contexts.Scope(self._args, self._partial.Render, render_state)
-
-class _PartialNodeInContext(_DecoratorNode):
-  def __init__(self, partial, context):
-    if isinstance(partial, Motemplate):
-      # Preserve any get() method that the caller has added.
-      if hasattr(partial, 'get'):
-        self.get = partial.get
-      partial = partial._top_node
-    _DecoratorNode.__init__(self, partial)
-    self._partial = partial
-    self._context = context
-
-  def Render(self, render_state):
-    original_contexts = render_state.contexts
-    try:
-      render_state.contexts = self._context
-      render_state.contexts.Scope(
-          # The first local context of |original_contexts| will be the
-          # arguments that were passed to the partial, if any.
-          original_contexts.FirstLocal() or {},
-          self._partial.Render, render_state)
-    finally:
-      render_state.contexts = original_contexts
-
-class _PartialNode(_LeafNode):
-  '''{{+var:foo}} ... {{/foo}}
-  '''
-  def __init__(self, bind_to, id_, content):
-    _LeafNode.__init__(self, id_.line, id_.line)
-    self._bind_to = bind_to
-    self._id = id_
-    self._content = content
-    self._args = None
-    self._pass_through_id = None
-
-  @classmethod
-  def Inline(cls, id_):
-    return cls(None, id_, None)
-
-  def Render(self, render_state):
-    value = render_state.contexts.Resolve(self._id.name)
-    if value is None:
-      render_state.AddResolutionError(self._id)
-      return
-    if not isinstance(value, (Motemplate, _Node)):
-      render_state.AddResolutionError(self._id, description='not a partial')
-      return
-
-    if isinstance(value, Motemplate):
-      node, name = value._top_node, value._name
-    else:
-      node, name = value, None
-
-    partial_render_state = render_state.ForkPartial(name, self._id)
-
-    arg_context = {}
-    if self._pass_through_id is not None:
-      context = render_state.contexts.Resolve(self._pass_through_id.name)
-      if context is not None:
-        arg_context[self._pass_through_id.name] = context
-    if self._args is not None:
-      def resolve_args(args):
-        resolved = {}
-        for key, value in args.iteritems():
-          if isinstance(value, dict):
-            assert len(value.keys()) == 1
-            id_of_partial, partial_args = value.items()[0]
-            partial = render_state.contexts.Resolve(id_of_partial.name)
-            if partial is not None:
-              resolved[key] = _PartialNodeWithArguments(
-                  partial, resolve_args(partial_args))
-          else:
-            context = render_state.contexts.Resolve(value.name)
-            if context is not None:
-              resolved[key] = context
-        return resolved
-      arg_context.update(resolve_args(self._args))
-    if self._bind_to and self._content:
-      arg_context[self._bind_to.name] = _PartialNodeInContext(
-          self._content, render_state.contexts)
-    if arg_context:
-      partial_render_state.contexts.Push(arg_context)
-
-    node.Render(partial_render_state)
-
-    render_state.Merge(
-        partial_render_state,
-        text_transform=lambda text: text[:-1] if text.endswith('\n') else text)
-
-  def SetArguments(self, args):
-    self._args = args
-
-  def PassThroughArgument(self, id_):
-    self._pass_through_id = id_
-
-  def __repr__(self):
-    return '{{+%s}}' % self._id
-
-_TOKENS = {}
-
-class _Token(object):
-  '''The tokens that can appear in a template.
-  '''
-  class Data(object):
-    def __init__(self, name, text, clazz):
-      self.name = name
-      self.text = text
-      self.clazz = clazz
-      _TOKENS[text] = self
-
-    def ElseNodeClass(self):
-      if self.clazz == _VertedSectionNode:
-        return _InvertedSectionNode
-      if self.clazz == _InvertedSectionNode:
-        return _VertedSectionNode
-      return None
-
-    def __repr__(self):
-      return self.name
-
-    def __str__(self):
-      return repr(self)
-
-  OPEN_START_SECTION = Data(
-      'OPEN_START_SECTION'         , '{{#', _SectionNode)
-  OPEN_START_VERTED_SECTION = Data(
-      'OPEN_START_VERTED_SECTION'  , '{{?', _VertedSectionNode)
-  OPEN_START_INVERTED_SECTION = Data(
-      'OPEN_START_INVERTED_SECTION', '{{^', _InvertedSectionNode)
-  OPEN_ASSERTION = Data(
-      'OPEN_ASSERTION'             , '{{!', _AssertionNode)
-  OPEN_JSON = Data(
-      'OPEN_JSON'                  , '{{*', _JsonNode)
-  OPEN_PARTIAL = Data(
-      'OPEN_PARTIAL'               , '{{+', _PartialNode)
-  OPEN_ELSE = Data(
-      'OPEN_ELSE'                  , '{{:', None)
-  OPEN_END_SECTION = Data(
-      'OPEN_END_SECTION'           , '{{/', None)
-  INLINE_END_SECTION = Data(
-      'INLINE_END_SECTION'         , '/}}', None)
-  OPEN_UNESCAPED_VARIABLE = Data(
-      'OPEN_UNESCAPED_VARIABLE'    , '{{{', _UnescapedVariableNode)
-  CLOSE_MUSTACHE3 = Data(
-      'CLOSE_MUSTACHE3'            , '}}}', None)
-  OPEN_COMMENT = Data(
-      'OPEN_COMMENT'               , '{{-', _CommentNode)
-  CLOSE_COMMENT = Data(
-      'CLOSE_COMMENT'              , '-}}', None)
-  OPEN_VARIABLE = Data(
-      'OPEN_VARIABLE'              , '{{' , _EscapedVariableNode)
-  CLOSE_MUSTACHE = Data(
-      'CLOSE_MUSTACHE'             , '}}' , None)
-  CHARACTER = Data(
-      'CHARACTER'                  , '.'  , None)
-
-class _TokenStream(object):
-  '''Tokeniser for template parsing.
-  '''
-  def __init__(self, string):
-    self.next_token = None
-    self.next_line = 1
-    self.next_column = 0
-    self._string = string
-    self._cursor = 0
-    self.Advance()
-
-  def HasNext(self):
-    return self.next_token is not None
-
-  def NextCharacter(self):
-    if self.next_token is _Token.CHARACTER:
-      return self._string[self._cursor - 1]
-    return None
-
-  def Advance(self):
-    if self._cursor > 0 and self._string[self._cursor - 1] == '\n':
-      self.next_line += 1
-      self.next_column = 0
-    elif self.next_token is not None:
-      self.next_column += len(self.next_token.text)
-
-    self.next_token = None
-
-    if self._cursor == len(self._string):
-      return None
-    assert self._cursor < len(self._string)
-
-    if (self._cursor + 1 < len(self._string) and
-        self._string[self._cursor + 1] in '{}'):
-      self.next_token = (
-          _TOKENS.get(self._string[self._cursor:self._cursor+3]) or
-          _TOKENS.get(self._string[self._cursor:self._cursor+2]))
-
-    if self.next_token is None:
-      self.next_token = _Token.CHARACTER
-
-    self._cursor += len(self.next_token.text)
-    return self
-
-  def AdvanceOver(self, token, description=None):
-    parse_error = None
-    if not self.next_token:
-      parse_error = 'Reached EOF but expected %s' % token.name
-    elif self.next_token is not token:
-      parse_error = 'Expecting token %s but got %s at line %s' % (
-                     token.name, self.next_token.name, self.next_line)
-    if parse_error:
-      parse_error += ' %s' % description or ''
-      raise ParseException(parse_error)
-    return self.Advance()
-
-  def AdvanceOverSeparator(self, char, description=None):
-    self.SkipWhitespace()
-    next_char = self.NextCharacter()
-    if next_char != char:
-      parse_error = 'Expected \'%s\'. got \'%s\'' % (char, next_char)
-      if description is not None:
-        parse_error += ' (%s)' % description
-      raise ParseException(parse_error)
-    self.AdvanceOver(_Token.CHARACTER)
-    self.SkipWhitespace()
-
-  def AdvanceOverNextString(self, excluded=''):
-    start = self._cursor - len(self.next_token.text)
-    while (self.next_token is _Token.CHARACTER and
-           # Can use -1 here because token length of CHARACTER is 1.
-           self._string[self._cursor - 1] not in excluded):
-      self.Advance()
-    end = self._cursor - (len(self.next_token.text) if self.next_token else 0)
-    return self._string[start:end]
-
-  def AdvanceToNextWhitespace(self):
-    return self.AdvanceOverNextString(excluded=' \n\r\t')
-
-  def SkipWhitespace(self):
-    while (self.next_token is _Token.CHARACTER and
-           # Can use -1 here because token length of CHARACTER is 1.
-           self._string[self._cursor - 1] in ' \n\r\t'):
-      self.Advance()
-
-  def __repr__(self):
-    return '%s(next_token=%s, remainder=%s)' % (type(self).__name__,
-                                                self.next_token,
-                                                self._string[self._cursor:])
-
-  def __str__(self):
-    return repr(self)
-
-class Motemplate(object):
-  '''A motemplate template.
-  '''
-  def __init__(self, template, name=None):
-    self.source = template
-    self._name = name
-    tokens = _TokenStream(template)
-    self._top_node = self._ParseSection(tokens)
-    if not self._top_node:
-      raise ParseException('Template is empty')
-    if tokens.HasNext():
-      raise ParseException('There are still tokens remaining at %s, '
-                           'was there an end-section without a start-section?' %
-                           tokens.next_line)
-
-  def _ParseSection(self, tokens):
-    nodes = []
-    while tokens.HasNext():
-      if tokens.next_token in (_Token.OPEN_END_SECTION,
-                               _Token.OPEN_ELSE):
-        # Handled after running parseSection within the SECTION cases, so this
-        # is a terminating condition. If there *is* an orphaned
-        # OPEN_END_SECTION, it will be caught by noticing that there are
-        # leftover tokens after termination.
-        break
-      elif tokens.next_token in (_Token.CLOSE_MUSTACHE,
-                                 _Token.CLOSE_MUSTACHE3):
-        raise ParseException('Orphaned %s at line %s' % (tokens.next_token.name,
-                                                         tokens.next_line))
-      nodes += self._ParseNextOpenToken(tokens)
-
-    for i, node in enumerate(nodes):
-      if isinstance(node, _StringNode):
-        continue
-
-      previous_node = nodes[i - 1] if i > 0 else None
-      next_node = nodes[i + 1] if i < len(nodes) - 1 else None
-      rendered_node = None
-
-      if node.GetStartLine() != node.GetEndLine():
-        rendered_node = _BlockNode(node)
-        if previous_node:
-          previous_node.TrimEndingSpaces()
-        if next_node:
-          next_node.TrimStartingNewLine()
-      elif ((not previous_node or previous_node.EndsWithEmptyLine()) and
-            (not next_node or next_node.StartsWithNewLine())):
-        indentation = 0
-        if previous_node:
-          indentation = previous_node.TrimEndingSpaces()
-        if next_node:
-          next_node.TrimStartingNewLine()
-        rendered_node = _IndentedNode(node, indentation)
-      else:
-        rendered_node = _InlineNode(node)
-
-      nodes[i] = rendered_node
-
-    if len(nodes) == 0:
-      return None
-    if len(nodes) == 1:
-      return nodes[0]
-    return _NodeCollection(nodes)
-
-  def _ParseNextOpenToken(self, tokens):
-    next_token = tokens.next_token
-
-    if next_token is _Token.CHARACTER:
-      # Plain strings.
-      start_line = tokens.next_line
-      string = tokens.AdvanceOverNextString()
-      return [_StringNode(string, start_line, tokens.next_line)]
-    elif next_token in (_Token.OPEN_VARIABLE,
-                        _Token.OPEN_UNESCAPED_VARIABLE,
-                        _Token.OPEN_JSON):
-      # Inline nodes that don't take arguments.
-      tokens.Advance()
-      close_token = (_Token.CLOSE_MUSTACHE3
-                     if next_token is _Token.OPEN_UNESCAPED_VARIABLE else
-                     _Token.CLOSE_MUSTACHE)
-      id_ = self._NextIdentifier(tokens)
-      tokens.AdvanceOver(close_token)
-      return [next_token.clazz(id_)]
-    elif next_token is _Token.OPEN_ASSERTION:
-      # Inline nodes that take arguments.
-      tokens.Advance()
-      id_ = self._NextIdentifier(tokens)
-      node = next_token.clazz(id_, tokens.AdvanceOverNextString())
-      tokens.AdvanceOver(_Token.CLOSE_MUSTACHE)
-      return [node]
-    elif next_token in (_Token.OPEN_PARTIAL,
-                        _Token.OPEN_START_SECTION,
-                        _Token.OPEN_START_VERTED_SECTION,
-                        _Token.OPEN_START_INVERTED_SECTION):
-      # Block nodes, though they may have inline syntax like {{#foo bar /}}.
-      tokens.Advance()
-      bind_to, id_ = None, self._NextIdentifier(tokens)
-      if tokens.NextCharacter() == ':':
-        # This section has the format {{#bound:id}} as opposed to just {{id}}.
-        # That is, |id_| is actually the identifier to bind what the section
-        # is producing, not the identifier of where to find that content.
-        tokens.AdvanceOverSeparator(':')
-        bind_to, id_ = id_, self._NextIdentifier(tokens)
-      partial_args = None
-      if next_token is _Token.OPEN_PARTIAL:
-        partial_args = self._ParsePartialNodeArgs(tokens)
-        if tokens.next_token is not _Token.CLOSE_MUSTACHE:
-          # Inline syntax for partial types.
-          if bind_to is not None:
-            raise ParseException(
-                'Cannot bind %s to a self-closing partial' % bind_to)
-          tokens.AdvanceOver(_Token.INLINE_END_SECTION)
-          partial_node = _PartialNode.Inline(id_)
-          partial_node.SetArguments(partial_args)
-          return [partial_node]
-      elif tokens.next_token is not _Token.CLOSE_MUSTACHE:
-        # Inline syntax for non-partial types. Support select node types:
-        # variables, partials, JSON.
-        line, column = tokens.next_line, (tokens.next_column + 1)
-        name = tokens.AdvanceToNextWhitespace()
-        clazz = _UnescapedVariableNode
-        if name.startswith('*'):
-          clazz = _JsonNode
-        elif name.startswith('+'):
-          clazz = _PartialNode.Inline
-        if clazz is not _UnescapedVariableNode:
-          name = name[1:]
-          column += 1
-        inline_node = clazz(_Identifier(name, line, column))
-        if isinstance(inline_node, _PartialNode):
-          inline_node.SetArguments(self._ParsePartialNodeArgs(tokens))
-          if bind_to is not None:
-            inline_node.PassThroughArgument(bind_to)
-        tokens.SkipWhitespace()
-        tokens.AdvanceOver(_Token.INLINE_END_SECTION)
-        return [next_token.clazz(bind_to, id_, inline_node)]
-      # Block syntax.
-      tokens.AdvanceOver(_Token.CLOSE_MUSTACHE)
-      section = self._ParseSection(tokens)
-      else_node_class = next_token.ElseNodeClass()  # may not have one
-      else_section = None
-      if (else_node_class is not None and
-          tokens.next_token is _Token.OPEN_ELSE):
-        self._OpenElse(tokens, id_)
-        else_section = self._ParseSection(tokens)
-      self._CloseSection(tokens, id_)
-      nodes = []
-      if section is not None:
-        node = next_token.clazz(bind_to, id_, section)
-        if partial_args:
-          node.SetArguments(partial_args)
-        nodes.append(node)
-      if else_section is not None:
-        nodes.append(else_node_class(bind_to, id_, else_section))
-      return nodes
-    elif next_token is _Token.OPEN_COMMENT:
-      # Comments.
-      start_line = tokens.next_line
-      self._AdvanceOverComment(tokens)
-      return [_CommentNode(start_line, tokens.next_line)]
-
-  def _AdvanceOverComment(self, tokens):
-    tokens.AdvanceOver(_Token.OPEN_COMMENT)
-    depth = 1
-    while tokens.HasNext() and depth > 0:
-      if tokens.next_token is _Token.OPEN_COMMENT:
-        depth += 1
-      elif tokens.next_token is _Token.CLOSE_COMMENT:
-        depth -= 1
-      tokens.Advance()
-
-  def _CloseSection(self, tokens, id_):
-    tokens.AdvanceOver(_Token.OPEN_END_SECTION,
-                       description='to match %s' % id_.GetDescription())
-    next_string = tokens.AdvanceOverNextString()
-    if next_string != '' and next_string != id_.name:
-      raise ParseException(
-          'Start section %s doesn\'t match end %s' % (id_, next_string))
-    tokens.AdvanceOver(_Token.CLOSE_MUSTACHE)
-
-  def _OpenElse(self, tokens, id_):
-    tokens.AdvanceOver(_Token.OPEN_ELSE)
-    next_string = tokens.AdvanceOverNextString()
-    if next_string != '' and next_string != id_.name:
-      raise ParseException(
-          'Start section %s doesn\'t match else %s' % (id_, next_string))
-    tokens.AdvanceOver(_Token.CLOSE_MUSTACHE)
-
-  def _ParsePartialNodeArgs(self, tokens):
-    args = {}
-    tokens.SkipWhitespace()
-    while (tokens.next_token is _Token.CHARACTER and
-           tokens.NextCharacter() != ')'):
-      key = tokens.AdvanceOverNextString(excluded=':')
-      tokens.AdvanceOverSeparator(':')
-      if tokens.NextCharacter() == '(':
-        tokens.AdvanceOverSeparator('(')
-        inner_id = self._NextIdentifier(tokens)
-        inner_args = self._ParsePartialNodeArgs(tokens)
-        tokens.AdvanceOverSeparator(')')
-        args[key] = {inner_id: inner_args}
-      else:
-        args[key] = self._NextIdentifier(tokens)
-    return args or None
-
-  def _NextIdentifier(self, tokens):
-    tokens.SkipWhitespace()
-    column_start = tokens.next_column + 1
-    id_ = _Identifier(tokens.AdvanceOverNextString(excluded=' \n\r\t:()'),
-                      tokens.next_line,
-                      column_start)
-    tokens.SkipWhitespace()
-    return id_
-
-  def Render(self, *user_contexts):
-    '''Renders this template given a variable number of contexts to read out
-    values from (such as those appearing in {{foo}}).
-    '''
-    internal_context = _InternalContext()
-    contexts = list(user_contexts)
-    contexts.append({
-      '_': internal_context,
-      'false': False,
-      'true': True,
-    })
-    render_state = _RenderState(self._name or '<root>', _Contexts(contexts))
-    internal_context.SetRenderState(render_state)
-    self._top_node.Render(render_state)
-    return render_state.GetResult()
-
-  def render(self, *contexts):
-    return self.Render(*contexts)
-
-  def __eq__(self, other):
-    return self.source == other.source and self._name == other._name
-
-  def __ne__(self, other):
-    return not (self == other)
-
-  def __repr__(self):
-    return str('%s(%s)' % (type(self).__name__, self._top_node))
-
-  def __str__(self):
-    return repr(self)
diff --git a/third_party/pdfium b/third_party/pdfium
index d5f3e3a..fb7720b 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit d5f3e3a18024afa0f6b05d8a7fe05086bdb377d8
+Subproject commit fb7720b24edf6a31672298e181775f951ca3924e
diff --git a/third_party/perfetto b/third_party/perfetto
index 76c9a33..40477ff 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 76c9a3333b19b28f567ed19a22d2ae04e9663803
+Subproject commit 40477ffb51d78b2b35aa049bcae0c4f8928235e6
diff --git a/third_party/r8/3pp/3pp.py b/third_party/r8/3pp/3pp.py
index 226d0be..2c0f34e 100755
--- a/third_party/r8/3pp/3pp.py
+++ b/third_party/r8/3pp/3pp.py
@@ -96,7 +96,8 @@
         shlex.split(f"""
         {java_home}/bin/java
             -Dcom.android.tools.r8.enableKeepAnnotations=1
-            -jar build/libs/r8.jar
+            -cp build/libs/r8.jar
+            com.android.tools.r8.R8
             --debug
             --classfile
             --no-minification
diff --git a/third_party/skia b/third_party/skia
index 44b4a40..79ea64d 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 44b4a40178cc671cf1576e4d5a20bc094dff0342
+Subproject commit 79ea64d7a71871ca51e2037d5ece6847ec9211e7
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 49bb428..6526c75 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 49bb428cd4514a34c4626a7589e2251d5b50dced
+Subproject commit 6526c75bbc69c0c529ee9005e0b2689cfc8d0722
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index 50910c0..ef846ac0 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit 50910c05fdc909aba59cc71e6320b0c5908912cd
+Subproject commit ef846ac0883cde5e69ced0e7d7af59fe92f34e25
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index 9619e6a..3208356 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit 9619e6a6ab523961fb5635ba9bc32d4a647f3074
+Subproject commit 32083568bca5204c5cd6baa32c2e0797ef148200
diff --git a/third_party/webrtc b/third_party/webrtc
index 51a2bd1..97d0427 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 51a2bd130d96136638fc462f8c3d2ca5d915ac11
+Subproject commit 97d04270808f04250d36fdc8c72c386e793e4b7a
diff --git a/third_party/zlib/README.chromium b/third_party/zlib/README.chromium
index 31b9d558..92c5bfd 100644
--- a/third_party/zlib/README.chromium
+++ b/third_party/zlib/README.chromium
@@ -2,6 +2,7 @@
 Short Name: zlib
 URL: http://zlib.net/
 Version: 1.3.0.1
+Revision: ac8f12c97d1afd9bafa9c710f827d40a407d3266
 CPEPrefix: cpe:/a:zlib:zlib:1.3.0.1
 Security Critical: yes
 Shipped: yes
diff --git a/tools/licenses/licenses.py b/tools/licenses/licenses.py
index 07b33f9c..f72e5cdb 100755
--- a/tools/licenses/licenses.py
+++ b/tools/licenses/licenses.py
@@ -424,7 +424,6 @@
     os.path.join('third_party', 'lss'),
     os.path.join('third_party', 'lzma_sdk'),
     os.path.join('third_party', 'mesa'),
-    os.path.join('third_party', 'motemplate'),
     os.path.join('third_party', 'mozc'),
     os.path.join('third_party', 'mozilla'),
     os.path.join('third_party', 'npapi'),
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f6710fa..3cdf9bd 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4535,6 +4535,7 @@
   <int value="12" label="SyncXHR"/>
   <int value="13" label="DocumentDomain"/>
   <int value="14" label="IncludeJSCallStacksInCrashReports"/>
+  <int value="15" label="ExpectNoEmbeddedResources"/>
 </enum>
 
 <enum name="DocumentScanSaneBackend">
@@ -10907,6 +10908,7 @@
   <int value="5097" label="V8Window_PopinContextType_Method"/>
   <int value="5098" label="WebAuthentication_AttestationFormats"/>
   <int value="5099" label="CssDisplayPropertyMultipleValues"/>
+  <int value="5100" label="DocumentPolicyExpectNoEmbeddedResources"/>
 </enum>
 
 <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom:WebFeature) -->
@@ -26875,6 +26877,7 @@
   <int value="805" label="text-wrap-mode"/>
   <int value="806" label="text-wrap-style"/>
   <int value="807" label="masonry-track"/>
+  <int value="808" label="masonry-slack"/>
 </enum>
 
 <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom:CSSSampleId) -->
@@ -34710,6 +34713,7 @@
   <int value="253258497" label="chrome://components/"/>
   <int value="260253931" label="chrome://devices/"/>
   <int value="275260142" label="chrome://eoc-internals/"/>
+  <int value="296428820" label="chrome-untrusted://lens-overlay/"/>
   <int value="325375649" label="chrome://settings-frame/"/>
   <int value="361250371" label="chrome://nearby/"/>
   <int value="361621847" label="chrome://make-metro/"/>
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index 5c296d2..4df59367 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -712,6 +712,7 @@
   <int value="3" label="WebAPK"/>
   <int value="4" label="Webapp (not WebAPK)"/>
   <int value="5" label="Pre-First-Tab (Startup/Warmup)"/>
+  <int value="6" label="Auth Tab"/>
 </enum>
 
 <enum name="ChromeStartupDelegateFailureType">
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 7ea18770..9322023 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -4189,9 +4189,11 @@
 </histogram>
 
 <histogram name="Android.SearchEngineChoice.ChosenSearchEngine"
-    enum="OmniboxSearchEngineType" expires_after="2023-06-01">
-  <owner>fgorski@chromium.org</owner>
+    enum="OmniboxSearchEngineType" expires_after="never">
+<!-- expires-never: Continuously monitored by Android partner team. -->
+
   <owner>wylieb@chromium.org</owner>
+  <owner>chrome-omnibox-team@google.com</owner>
   <summary>
     The type of the search engine chosen by the user in Search Engine Choice
     flow.
@@ -4199,19 +4201,22 @@
 </histogram>
 
 <histogram name="Android.SearchEngineChoice.Events"
-    enum="AndroidSearchEngineChoiceEvents" expires_after="2023-06-01">
+    enum="AndroidSearchEngineChoiceEvents" expires_after="never">
+<!-- expires-never: Continuously monitored by Android partner team. -->
+
   <owner>wylieb@chromium.org</owner>
-  <owner>fgorski@chromium.org</owner>
+  <owner>chrome-omnibox-team@google.com</owner>
   <summary>
     Counts occurences of various events related to Search Engine Choice feature.
   </summary>
 </histogram>
 
 <histogram name="Android.SearchEngineChoice.EventsV2"
-    enum="AndroidSearchEngineChoiceEventsV2" expires_after="2024-05-26">
+    enum="AndroidSearchEngineChoiceEventsV2" expires_after="never">
+<!-- expires-never: Continuously monitored by Android partner team. -->
+
   <owner>wylieb@chromium.org</owner>
-  <owner>pavely@chromium.org</owner>
-  <owner>fgorski@chromium.org</owner>
+  <owner>chrome-omnibox-team@google.com</owner>
   <summary>
     Counts occurences of various events related to Search Engine Choice V2
     feature.
@@ -4219,9 +4224,11 @@
 </histogram>
 
 <histogram name="Android.SearchEngineChoice.SearchEngineBeforeChoicePrompt"
-    enum="OmniboxSearchEngineType" expires_after="2025-01-20">
+    enum="OmniboxSearchEngineType" expires_after="never">
+<!-- expires-never: Continuously monitored by Android partner team. -->
+
   <owner>wylieb@chromium.org</owner>
-  <owner>fgorski@chromium.org</owner>
+  <owner>chrome-omnibox-team@google.com</owner>
   <summary>
     The type of the search engine used before Search Engine Choice flow was
     presented.
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 0b96df6..107477a 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -4812,7 +4812,7 @@
 </histogram>
 
 <histogram name="Ash.Homescreen.AnimationSmoothness" units="%"
-    expires_after="2024-09-01">
+    expires_after="2025-09-05">
   <owner>sammiequon@chromium.org</owner>
   <owner>tbarzic@chromium.org</owner>
   <summary>
@@ -8949,7 +8949,7 @@
 </histogram>
 
 <histogram name="Ash.SplitViewResize.AnimationSmoothness.DividerAnimation"
-    units="%" expires_after="2024-10-06">
+    units="%" expires_after="2025-09-05">
   <owner>sammiequon@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>
@@ -9822,7 +9822,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.CrossFade" units="%"
-    expires_after="2025-01-26">
+    expires_after="2025-09-05">
   <owner>sammiequon@chromium.org</owner>
   <owner>wutao@chromium.org</owner>
   <summary>
@@ -9835,7 +9835,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.CrossFade.DragMaximize"
-    units="%" expires_after="2024-10-16">
+    units="%" expires_after="2025-09-05">
   <owner>sammiequon@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>
@@ -9851,7 +9851,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.CrossFade.DragUnmaximize"
-    units="%" expires_after="2024-10-16">
+    units="%" expires_after="2025-09-05">
   <owner>sammiequon@chromium.org</owner>
   <owner>xdai@chromium.org</owner>
   <summary>
@@ -9865,7 +9865,7 @@
 </histogram>
 
 <histogram name="Ash.Window.AnimationSmoothness.Hide" units="%"
-    expires_after="2024-10-16">
+    expires_after="2025-09-05">
   <owner>sammiequon@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 4c62b16..eadfe3d7 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -96,6 +96,48 @@
                feature"/>
 </variants>
 
+<!-- LINT.IfChange(Autofill.FillingProduct) -->
+
+<variants name="Autofill.FillingProduct">
+  <variant name="Address"
+      summary="User chose to fill a form or field with an address profile."/>
+  <variant name="Autocomplete"
+      summary="User chose to fill a field with autocomplete."/>
+  <variant name="Compose"
+      summary="User chose to fill a field with a compose suggestion."/>
+  <variant name="CreditCard"
+      summary="User chose to fill a form or field with a credit card profile."/>
+  <variant name="Iban" summary="User chose to fill a field with an iban."/>
+  <variant name="MerchantPromoCode"
+      summary="User chose to fill a field with a merchant promo code."/>
+  <variant name="None" summary="No filling product matches."/>
+  <variant name="Password"
+      summary="User chose to fill a form or field with password credentials."/>
+  <variant name="PlusAddresses"
+      summary="User chose to fill a field with a prediction improvement."/>
+  <variant name="PredictionImprovements"
+      summary="User chose to fill a field with a plus addresses suggestion."/>
+  <variant name="StandaloneCvc"
+      summary="User chose to fill a form or field with a CVC."/>
+</variants>
+
+<!-- LINT.ThenChange(/components/autofill/core/browser/filling_product.mm:FillingProductToString) -->
+
+<!-- LINT.IfChange(Autofill.FillingProduct.Condensed) -->
+
+<variants name="Autofill.FillingProduct.Condensed">
+  <variant name="Address"
+      summary="User chose to fill a form or field with an address profile."/>
+  <variant name="CreditCard"
+      summary="User chose to fill a form or field with a credit card profile."/>
+  <variant name="Password"
+      summary="User chose to fill a form or field with password credentials."/>
+  <variant name="StandaloneCvc"
+      summary="User chose to fill a form or field with a CVC."/>
+</variants>
+
+<!-- LINT.ThenChange(/components/autofill/core/browser/filling_product.mm:FillingProductToString) -->
+
 <variants name="Autofill.FormEventWithMetadata">
   <variant name="FilledWithMetadata" summary="A card with metadata was filled"/>
   <variant name="FilledWithMetadataOnce"
@@ -242,6 +284,8 @@
   <variant name="OtpFallbackFromFido"
       summary="OTP fallback from FIDO authentication"/>
   <variant name="RiskBased" summary="Risk-based authentication"/>
+  <variant name="ThreeDomainSecure"
+      summary="3DS (Three Domain Secure) authentication"/>
   <variant name="UnspecifiedFlowType" summary="No authentication specified"/>
 </variants>
 
@@ -2372,12 +2416,7 @@
     classification. Recorded when a field-by-field filling suggestion is
     accepted by the user from an Autofill popup.
   </summary>
-  <token key="FillingProduct">
-    <variant name="Address"/>
-    <variant name="CreditCard"/>
-    <variant name="Password"/>
-    <variant name="StandaloneCvc"/>
-  </token>
+  <token key="FillingProduct" variants="Autofill.FillingProduct.Condensed"/>
   <token key="ClassificationMatchesFillingProduct"
       variants="ClassificationMatchesFillingProduct"/>
 </histogram>
@@ -2584,12 +2623,7 @@
     field or a group of fields (address fields, phone fields, name fields or
     email fields).
   </summary>
-  <token key="FillingProduct">
-    <variant name="Address"/>
-    <variant name="CreditCard"/>
-    <variant name="Password"/>
-    <variant name="StandaloneCvc"/>
-  </token>
+  <token key="FillingProduct" variants="Autofill.FillingProduct.Condensed"/>
   <token key="ClassificationMatchesFillingProduct"
       variants="ClassificationMatchesFillingProduct"/>
 </histogram>
@@ -4335,26 +4369,7 @@
     Records the duration that the autofill root popup remains open. Emitted by
     the popup controller on hide. Note that it is not emitted for sub popups.
   </summary>
-  <token key="FillingProduct">
-    <variant name="Address"
-        summary="User chose to fill a form or field with an address profile."/>
-    <variant name="Autocomplete"
-        summary="User chose to fill a field with autocomplete."/>
-    <variant name="Compose"
-        summary="User chose to fill a field with a compose suggestion."/>
-    <variant name="CreditCard"
-        summary="User chose to fill a form or field with a credit card
-                 profile."/>
-    <variant name="Iban" summary="User chose to fill a field with an iban."/>
-    <variant name="MerchantPromoCode"
-        summary="User chose to fill a field with a merchant promo code."/>
-    <variant name="None" summary="No filling product matches."/>
-    <variant name="Password"
-        summary="User chose to fill a form or field with password
-                 credentials."/>
-    <variant name="PlusAddresses"
-        summary="User chose to fill a field with a plus addresses suggestion."/>
-  </token>
+  <token key="FillingProduct" variants="Autofill.FillingProduct"/>
 </histogram>
 
 <histogram name="Autofill.PopupHidingReason"
@@ -4377,29 +4392,7 @@
     disappers or cannot be displayed. Recorded everytime an Autofill popup
     disappears or cannot be displayed.
   </summary>
-  <token key="FillingProduct">
-    <variant name="Address"
-        summary="User chose to fill a form or field with an address profile."/>
-    <variant name="Autocomplete"
-        summary="User chose to fill a field with autocomplete."/>
-    <variant name="Compose"
-        summary="User chose to fill a field with a compose suggestion."/>
-    <variant name="CreditCard"
-        summary="User chose to fill a form or field with a credit card
-                 profile."/>
-    <variant name="Iban" summary="User chose to fill a field with an iban."/>
-    <variant name="MerchantPromoCode"
-        summary="User chose to fill a field with a merchant promo code."/>
-    <variant name="None" summary="No filling product matches."/>
-    <variant name="Password"
-        summary="User chose to fill a form or field with password
-                 credentials."/>
-    <variant name="PlusAddresses"
-        summary="User chose to fill a field with a plus addresses suggestion."/>
-    <variant name="StandaloneCvc"
-        summary="User chose to fill a form or field with a CVC for a virtual
-                 card saved on file."/>
-  </token>
+  <token key="FillingProduct" variants="Autofill.FillingProduct"/>
 </histogram>
 
 <histogram
@@ -4429,21 +4422,7 @@
   </summary>
   <token key="AutofillPopupInteractionLevel"
       variants="AutofillPopupInteractionLevel"/>
-  <token key="FillingProduct">
-    <variant name="Address" summary="popup with address suggestion(s)"/>
-    <variant name="Autocomplete"
-        summary="popup with autocomplete suggestion(s)"/>
-    <variant name="Compose" summary="popup with compose suggestion(s)"/>
-    <variant name="CreditCard" summary="popup with credit card suggestion(s)"/>
-    <variant name="Iban" summary="popup with iban suggestion(s)"/>
-    <variant name="MerchantPromoCode"
-        summary="popup with merchant promo code suggestion(s)"/>
-    <variant name="None"
-        summary="popup with suggestion(s) that match no filling product"/>
-    <variant name="Password" summary="popup with password suggestion(s)"/>
-    <variant name="PlusAddresses"
-        summary="popup with plus addresses suggestion(s)"/>
-  </token>
+  <token key="FillingProduct" variants="Autofill.FillingProduct"/>
 </histogram>
 
 <histogram name="Autofill.PreFilledFieldClassifications.{FormType}"
@@ -6479,28 +6458,7 @@
     The index of the accepted Autofill suggestion, split by context. Logged when
     the user taps on a suggestion. iOS only. {FillingProduct} {Context}
   </summary>
-  <token key="FillingProduct">
-    <variant name="Address"
-        summary="User chose to fill a form or field with an address profile."/>
-    <variant name="Autocomplete"
-        summary="User chose to fill a field with autocomplete."/>
-    <variant name="Compose"/>
-    <variant name="CreditCard"
-        summary="User chose to fill a form or field with a credit card
-                 profile."/>
-    <variant name="Iban" summary="User chose to fill a field with an iban."/>
-    <variant name="MerchantPromoCode"
-        summary="User chose to fill a field with a merchant promo code."/>
-    <variant name="None" summary="No filling product matches."/>
-    <variant name="Password"
-        summary="User chose to fill a form or field with password
-                 credentials."/>
-    <variant name="PlusAddresses"
-        summary="User chose to fill a field with a plus addresses suggestion."/>
-    <variant name="StandaloneCvc"
-        summary="User chose to fill a form or field with a CVC for a virtual
-                 card saved on file."/>
-  </token>
+  <token key="FillingProduct" variants="Autofill.FillingProduct"/>
   <token key="Context">
     <variant name="BottomSheet"/>
     <variant name="KeyboardAccessory"/>
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml
index 9ccf90d..52c75f9 100644
--- a/tools/metrics/histograms/metadata/browser/histograms.xml
+++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -435,7 +435,7 @@
 
 <histogram name="Browser.ERP.UnuploadedCrashShouldNotReportReason"
     enum="EnterpriseReportingUnuploadedCrashShouldNotReportReason"
-    expires_after="2024-10-18">
+    expires_after="2025-05-01">
   <owner>lbaraz@chromium.org</owner>
   <owner>cros-reporting-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/enums.xml b/tools/metrics/histograms/metadata/custom_tabs/enums.xml
index eab2df2..356242b 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/enums.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/enums.xml
@@ -37,6 +37,7 @@
   <int value="0" label="User action in Chrome (back press, close button)"/>
   <int value="1" label="User action in Android (home)"/>
   <int value="2" label="Auto-closed"/>
+  <int value="3" label="AuthTab success"/>
 </enum>
 
 <enum name="CustomTabsConnection">
diff --git a/tools/metrics/histograms/metadata/facilitated_payments/enums.xml b/tools/metrics/histograms/metadata/facilitated_payments/enums.xml
index 546ea3a4..b576c73 100644
--- a/tools/metrics/histograms/metadata/facilitated_payments/enums.xml
+++ b/tools/metrics/histograms/metadata/facilitated_payments/enums.xml
@@ -28,6 +28,7 @@
   <int value="1" label="Risk data empty"/>
   <int value="2" label="Code validtor failed"/>
   <int value="3" label="Invalid code"/>
+  <int value="4" label="Landscape screen orientation"/>
 </enum>
 
 <enum name="FacilitatedPayments.TransactionResult">
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 298f612..04c0e183f 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -2593,7 +2593,7 @@
     expires_after="2025-03-09">
   <owner>kyraseevers@chromium.org</owner>
   <owner>brgoldstein@google.com</owner>
-  <component>Blink&gt;History&gt;VisitedLinks</component>
+  <component>1456589</component>
   <summary>
     Number of rows in visited_links table in History DB. Metrics are logged on
     initialization of the History DB on 1% of starts.
diff --git a/tools/metrics/histograms/metadata/media/enums.xml b/tools/metrics/histograms/metadata/media/enums.xml
index 1d34dbc..ce810ab8 100644
--- a/tools/metrics/histograms/metadata/media/enums.xml
+++ b/tools/metrics/histograms/metadata/media/enums.xml
@@ -935,17 +935,6 @@
   <int value="2146164868" label="v410"/>
 </enum>
 
-<!-- LINT.IfChange(FileDialogOpenStateEnum) -->
-
-<enum name="FileDialogOpenState">
-  <int value="0" label="File dialog open with picture-in-picture"/>
-  <int value="1" label="File dialog open without picture-in-picture"/>
-  <int value="2" label="Picture-in-picture open with file dialog"/>
-  <int value="3" label="Picture-in-picture open without file dialog"/>
-</enum>
-
-<!-- LINT.ThenChange(//chrome/browser/picture_in_picture/picture_in_picture_window_manager.h:FileDialogOpenState) -->
-
 <enum name="GenericEnum">
   <summary>Generic enums where the label is just the raw number</summary>
   <int value="0" label="Enum placerholder. See crbug/1179826"/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index f29ff32e..8a41163 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -4791,17 +4791,6 @@
   </summary>
 </histogram>
 
-<histogram name="Media.PictureInPicture.FileDialogOpenState"
-    enum="FileDialogOpenState" expires_after="2025-03-02">
-  <owner>steimel@chromium.org</owner>
-  <owner>liberato@chromium.org</owner>
-  <summary>
-    Records whether or not a file dialog and picture-in-picture window are open
-    at the same time. Recorded when either a file dialog or picture-in-picture
-    window are opened.
-  </summary>
-</histogram>
-
 <histogram name="Media.PipelineStatus.AudioVideo.{VideoCodec}.{Pipeline}"
     enum="PipelineStatus" expires_after="never">
 <!-- expires-never: Media pipeline health metric. -->
diff --git a/tools/metrics/histograms/metadata/network/enums.xml b/tools/metrics/histograms/metadata/network/enums.xml
index 124b1a76..c79a7ad6 100644
--- a/tools/metrics/histograms/metadata/network/enums.xml
+++ b/tools/metrics/histograms/metadata/network/enums.xml
@@ -639,6 +639,16 @@
   <int value="1" label="Proxy B"/>
 </enum>
 
+<enum name="IpProtectionTokenBatchRequestError">
+  <int value="58877100" label="Failed to read timestamp"/>
+  <int value="354921842" label="At least one extension is required"/>
+  <int value="1218093704" label="Failed to read extensions"/>
+  <int value="2528366448" label="Failed to parse GetInitialDataResponse"/>
+  <int value="2867080750" label="Failed to read timestamp_precision"/>
+  <int value="3336018440"
+      label="Non-Privacy Pass tokens are no longer supported"/>
+</enum>
+
 <enum name="IpProtectionTokenBatchRequestResult">
   <int value="0" label="Success"/>
   <int value="1" label="Failed - No Account"/>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index 14cd473..7ae79d6e7 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -5167,6 +5167,18 @@
   </summary>
 </histogram>
 
+<histogram name="NetworkService.IpProtection.TryGetAuthTokensErrors"
+    enum="IpProtectionTokenBatchRequestError" expires_after="2025-01-19">
+  <owner>linxinan@chromium.org</owner>
+  <owner>src/chrome/browser/ip_protection/OWNERS</owner>
+  <summary>
+    This histogram is emitted when any error returned by GetTokens call into the
+    BSA library. The error message is hashed into unsigned 32 bit integer. For
+    more information on the possible errors from BSA see
+    https://docs.google.com/document/d/1zv3AqLkALZGsvCKYd1M3UytNpyY0ecK55LdsioubmC4.
+  </summary>
+</histogram>
+
 <histogram name="NetworkService.IpProtection.TryGetAuthTokensResult"
     enum="IpProtectionTokenBatchRequestResult" expires_after="2025-01-19">
   <owner>djmitche@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index b11f0c6..2772e28 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -2815,6 +2815,49 @@
   </summary>
 </histogram>
 
+<histogram name="ShortcutsProvider.DatabaseSize" units="units"
+    expires_after="2025-02-10">
+  <owner>jdonnelly@chromium.org</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The number of entries in `ShortcutsBackend`'s database. Recorded once on
+    profile load. See also ShortcutsProvider.DatabaseSize.OldEntries and
+    ShortcutsProvider.OldEntryDeletions.
+  </summary>
+</histogram>
+
+<histogram name="ShortcutsProvider.DatabaseSize.OldEntries" units="units"
+    expires_after="2025-02-10">
+  <owner>jdonnelly@chromium.org</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The number of entries in `ShortcutsBackend`'s database that are older than
+    90 days. Recorded once on profile load.
+
+    This value is expected to be near 0 as there are two mechanisms to delete
+    old entries. The first is individual deletions due to notificaitons that the
+    associated URL has been deleted from the history system. This mechanism is
+    not 100% reliable, however, since the URL deletions are performed by
+    HistoryService, which may be started up before ShortcutsBackend, which
+    handles the notifications. Therefore the second mechanism is a batch
+    deletion of all entries older than 90 days by ShortcutsBackend on profile
+    load. The count of deletions via the second mechanism is recorded in
+    ShortcutsProvider.OldEntryDeletions.
+
+    See also ShortcutsProvider.DatabaseSize.
+  </summary>
+</histogram>
+
+<histogram name="ShortcutsProvider.OldEntryDeletions" units="units"
+    expires_after="2025-02-10">
+  <owner>jdonnelly@chromium.org</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The number of entries in `ShortcutsBackend`'s database that were deleted due
+    to being older than 90 days. Recorded once on profile load.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 0dddaac..2ed041d 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -187,8 +187,9 @@
   <variant name=".Emoji"/>
   <variant name=".Feed"/>
   <variant name=".HistoryClustersSidePanel"/>
+  <variant name=".LensOverlayUntrusted"/>
   <variant name=".LensSearchBubble"/>
-  <variant name=".LensUntrusted"/>
+  <variant name=".LensSidePanelUntrusted"/>
   <variant name=".MakoUntrusted"/>
   <variant name=".PerformanceSidePanel"/>
   <variant name=".ReadAnythingUntrusted"/>
@@ -5028,7 +5029,7 @@
 </histogram>
 
 <histogram name="FirstUserAction.BackgroundTime" units="minutes"
-    expires_after="2024-09-12">
+    expires_after="2025-09-12">
 <!-- Name completed by histogram_suffixes name="FirstUserActionType" and name="FirstUserActionTypeDevice" -->
 
   <owner>justincohen@chromium.org</owner>
@@ -5045,7 +5046,7 @@
 </histogram>
 
 <histogram name="FirstUserAction.HandsetUserActionType"
-    enum="FirstUserActionType" expires_after="2024-09-12">
+    enum="FirstUserActionType" expires_after="2025-09-12">
   <owner>justincohen@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
   <summary>
@@ -5060,7 +5061,7 @@
 </histogram>
 
 <histogram name="FirstUserAction.TabletUserActionType"
-    enum="FirstUserActionType" expires_after="2024-09-12">
+    enum="FirstUserActionType" expires_after="2025-09-12">
   <owner>justincohen@chromium.org</owner>
   <owner>olivierrobin@chromium.org</owner>
   <summary>
@@ -6838,6 +6839,18 @@
   </summary>
 </histogram>
 
+<histogram name="OSCrypt.Posix.NoEncryptionPrefixFound" enum="BooleanAllowed"
+    expires_after="2025-08-15">
+  <owner>wfh@chromium.org</owner>
+  <owner>thestig@chromium.org</owner>
+  <summary>
+    True, iff the data did not have the required encryption prefix, and was
+    returned without modification to the caller. This is emitted every time
+    OSCrypt attempts to decrypt a non-empty value on platforms using
+    os_crypt_posix.cc (e.g. Android, Chrome OS, Fushsia).
+  </summary>
+</histogram>
+
 <histogram name="OSCrypt.Win.Decrypt.Result" enum="BooleanSuccess"
     expires_after="2025-02-10">
   <owner>wfh@chromium.org</owner>
@@ -8326,15 +8339,6 @@
   </summary>
 </histogram>
 
-<histogram name="ShortcutsProvider.DatabaseSize" units="units"
-    expires_after="2025-02-10">
-  <owner>mpearson@chromium.org</owner>
-  <summary>
-    The number of entries in shortcuts backend's database when initialized,
-    which happens during profile load.
-  </summary>
-</histogram>
-
 <histogram name="Shutdown.BrowserExit.Time2" units="ms"
     expires_after="2025-02-10">
   <owner>etienneb@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index 08f6d15..e307119a 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -218,8 +218,9 @@
   <variant name=".Emoji"/>
   <variant name=".Feed"/>
   <variant name=".HistoryClustersSidePanel"/>
+  <variant name=".LensOverlayUntrusted"/>
   <variant name=".LensSearchBubble"/>
-  <variant name=".LensUntrusted"/>
+  <variant name=".LensSidePanelUntrusted"/>
   <variant name=".MakoUntrusted"/>
   <variant name=".PerformanceSidePanel"/>
   <variant name=".ReadAnythingUntrusted"/>
diff --git a/tools/metrics/histograms/metadata/signin/enums.xml b/tools/metrics/histograms/metadata/signin/enums.xml
index 950da9e..5fbb75ee 100644
--- a/tools/metrics/histograms/metadata/signin/enums.xml
+++ b/tools/metrics/histograms/metadata/signin/enums.xml
@@ -360,9 +360,10 @@
   <int value="24" label="kRemoteConsentPageLoadFailure"/>
   <int value="25" label="kSetAccountsInCookieFailure (deprecated 07/2023)"/>
   <int value="26" label="kInvalidConsentResult"/>
-  <int value="27" label="kCanceled"/>
+  <int value="27" label="kCanceled (deprecated 09/2024)"/>
   <int value="28" label="kInteractivityDenied"/>
   <int value="29" label="kCannotCreateWindow"/>
+  <int value="30" label="kBrowserContextShutDown"/>
 </enum>
 
 <enum name="IOSDeviceRestoreSignedinState">
@@ -382,6 +383,7 @@
   <int value="6" label="kPageLoadTimedOut"/>
   <int value="7" label="kCannotCreateWindow"/>
   <int value="8" label="kInvalidURLScheme"/>
+  <int value="9" label="kBrowserContextShutDown"/>
 </enum>
 
 <enum name="OAuth2LoginAccountRevokedMigrationState">
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index 20a1a35..65aba49 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -948,7 +948,6 @@
     units="bytes" expires_after="2025-04-01">
   <owner>ankushkush@google.com</owner>
   <owner>treib@chromium.org</owner>
-  <component>Services&gt;Sync</component>
   <summary>
     Size of individual sync entities (not including tombstones) in bytes,
     including the sync metadata. Recorded right before an entity is committed to
@@ -961,7 +960,6 @@
     expires_after="2025-04-01">
   <owner>ankushkush@google.com</owner>
   <owner>treib@chromium.org</owner>
-  <component>Services&gt;Sync</component>
   <summary>
     Size of individual sync tombstones in bytes, including the sync metadata.
     Recorded right before an entity is committed to the server. See also
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 0f41980..abed847 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -15158,6 +15158,13 @@
       crbug.com/897837 crbug.com/897840
     </summary>
   </metric>
+  <metric name="Experimental.TotalForegroundDuration">
+    <summary>
+      Re-adding experimental version of TotalForegroundDuration to allow for an
+      analysis of the now obsolete field. This will help inform a discrepency
+      found when switching to the PageTiming.TotalForegroundDuration field.
+    </summary>
+  </metric>
   <metric name="HourOfDay">
     <summary>
       The hour of the day when the page load was initiated. The time is measured
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 8ad06f9..4188679 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/12329cd9546a702b415210b83776d02f2e08d6b6/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "125266f5f5cf85ec29dd380485af0bc49a44fe3a",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/12329cd9546a702b415210b83776d02f2e08d6b6/trace_processor_shell.exe"
+            "hash": "092075f853ac254c1e49a90b78b178f29258483b",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/40477ffb51d78b2b35aa049bcae0c4f8928235e6/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "cf5265bf837405a540d5b46ca8ab5abfbdaf1913",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/12329cd9546a702b415210b83776d02f2e08d6b6/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "e74f1283ddc2f21529cb6bff5acb8951f5b1d479",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/3a7f8ce095b5be0b1925d52022a46db3753d5199/trace_processor_shell"
+            "hash": "06c8eaaab25cf3674237a2b58fc639ad2c6e4d1c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/40477ffb51d78b2b35aa049bcae0c4f8928235e6/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/results_processor/command_line.py b/tools/perf/core/results_processor/command_line.py
index 02f88ee..f3b4248e 100644
--- a/tools/perf/core/results_processor/command_line.py
+++ b/tools/perf/core/results_processor/command_line.py
@@ -11,7 +11,6 @@
 import datetime
 import logging
 import os
-import re
 import sys
 
 from py_utils import cloud_storage
@@ -182,13 +181,9 @@
   if options.intermediate_dir:
     options.intermediate_dir = resolve_dir(options.intermediate_dir)
   else:
-    if options.results_label:
-      filesafe_label = re.sub(r'\W+', '_', options.results_label)
-    else:
-      filesafe_label = 'run'
     start_time = datetime.datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')
     options.intermediate_dir = os.path.join(
-        options.output_dir, 'artifacts', '%s_%s' % (filesafe_label, start_time))
+        options.output_dir, 'artifacts', 'run_%s' % start_time)
 
   if options.upload_results:
     options.upload_bucket = cloud_storage.BUCKET_ALIASES.get(
diff --git a/tools/perf/core/results_processor/command_line_unittest.py b/tools/perf/core/results_processor/command_line_unittest.py
index 8cfa315..3b280ca 100644
--- a/tools/perf/core/results_processor/command_line_unittest.py
+++ b/tools/perf/core/results_processor/command_line_unittest.py
@@ -87,7 +87,7 @@
     options = self.ParseArgs(
         ['--output-dir', '/output', '--results-label', 'test my feature'])
     self.assertEqual(options.intermediate_dir,
-                     '/output/artifacts/test_my_feature_20151021T072800Z')
+                     '/output/artifacts/run_20151021T072800Z')
 
   def testUploadBucket_noUploadResults(self):
     options = self.ParseArgs([])
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index 59e948a6..d1ab3de8 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -317,6 +317,7 @@
     "mojom/ax_action_data_mojom_traits_unittest.cc",
     "mojom/ax_event_intent_mojom_traits_unittest.cc",
     "mojom/ax_event_mojom_traits_unittest.cc",
+    "mojom/ax_location_changes_mojom_traits_unittest.cc",
     "mojom/ax_mode_mojom_traits_unittest.cc",
     "mojom/ax_node_data_mojom_traits_unittest.cc",
     "mojom/ax_relative_bounds_mojom_traits_unittest.cc",
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index 1129909..0bf7b15 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -415,7 +415,8 @@
              "ReadAnythingDocsIntegration",
              base::FEATURE_DISABLED_BY_DEFAULT);
 bool IsReadAnythingDocsIntegrationEnabled() {
-  return base::FeatureList::IsEnabled(::features::kReadAnythingDocsIntegration);
+  return base::FeatureList::IsEnabled(
+      ax::mojom::features::kReadAnythingDocsIntegration);
 }
 
 BASE_FEATURE(kReadAnythingDocsLoadMoreButton,
diff --git a/ui/accessibility/ax_features.mojom b/ui/accessibility/ax_features.mojom
index 0a70fe4..ba97df0a 100644
--- a/ui/accessibility/ax_features.mojom
+++ b/ui/accessibility/ax_features.mojom
@@ -16,4 +16,11 @@
 feature kScreenAIOCREnabled {
   const string name = "ScreenAIOCREnabled";
   const bool default_state = true;
+};
+
+// This feature protects Read Anything from performing large actions on
+// pages that shouldn't be updated.
+feature kReadAnythingDocsIntegration {
+  const string name = "ReadAnythingDocsIntegration";
+  const bool default_state = false;
 };
\ No newline at end of file
diff --git a/ui/accessibility/mojom/BUILD.gn b/ui/accessibility/mojom/BUILD.gn
index 94d8738..3cbe17a8 100644
--- a/ui/accessibility/mojom/BUILD.gn
+++ b/ui/accessibility/mojom/BUILD.gn
@@ -10,6 +10,7 @@
     "ax_action_data.mojom",
     "ax_event.mojom",
     "ax_event_intent.mojom",
+    "ax_location_changes.mojom",
     "ax_mode.mojom",
     "ax_node_data.mojom",
     "ax_relative_bounds.mojom",
@@ -87,6 +88,17 @@
     {
       types = [
         {
+          mojom = "ax.mojom.AXLocationChanges"
+          cpp = "::ui::AXLocationChanges"
+        },
+      ]
+      traits_sources = [ "ax_location_changes_mojom_traits.cc" ]
+      traits_headers = [ "ax_location_changes_mojom_traits.h" ]
+      traits_public_deps = [ "//ui/accessibility:ax_base" ]
+    },
+    {
+      types = [
+        {
           mojom = "ax.mojom.AXMode"
           cpp = "::ui::AXMode"
         },
diff --git a/ui/accessibility/mojom/ax_location_changes.mojom b/ui/accessibility/mojom/ax_location_changes.mojom
new file mode 100644
index 0000000..c63406b
--- /dev/null
+++ b/ui/accessibility/mojom/ax_location_changes.mojom
@@ -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.
+
+module ax.mojom;
+
+import "ui/accessibility/mojom/ax_relative_bounds.mojom";
+import "ui/accessibility/mojom/ax_tree_id.mojom";
+
+struct AXLocationChanges {
+  int32 id;
+  ax.mojom.AXTreeID ax_tree_id;
+  ax.mojom.AXRelativeBounds new_location;
+};
\ No newline at end of file
diff --git a/ui/accessibility/mojom/ax_location_changes_mojom_traits.cc b/ui/accessibility/mojom/ax_location_changes_mojom_traits.cc
new file mode 100644
index 0000000..17f61fb7
--- /dev/null
+++ b/ui/accessibility/mojom/ax_location_changes_mojom_traits.cc
@@ -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.
+
+#include "ui/accessibility/mojom/ax_location_changes_mojom_traits.h"
+
+#include "ui/accessibility/mojom/ax_relative_bounds_mojom_traits.h"
+#include "ui/accessibility/mojom/ax_tree_update_mojom_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<ax::mojom::AXLocationChangesDataView, ui::AXLocationChanges>::
+    Read(ax::mojom::AXLocationChangesDataView data,
+         ui::AXLocationChanges* out) {
+  out->id = data.id();
+  if (!data.ReadAxTreeId(&out->ax_tree_id)) {
+    return false;
+  }
+  if (!data.ReadNewLocation(&out->new_location)) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace mojo
diff --git a/ui/accessibility/mojom/ax_location_changes_mojom_traits.h b/ui/accessibility/mojom/ax_location_changes_mojom_traits.h
new file mode 100644
index 0000000..22428baa
--- /dev/null
+++ b/ui/accessibility/mojom/ax_location_changes_mojom_traits.h
@@ -0,0 +1,30 @@
+// 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_ACCESSIBILITY_MOJOM_AX_LOCATION_CHANGES_MOJOM_TRAITS_H_
+#define UI_ACCESSIBILITY_MOJOM_AX_LOCATION_CHANGES_MOJOM_TRAITS_H_
+
+#include "ui/accessibility/ax_updates_and_events.h"
+#include "ui/accessibility/mojom/ax_location_changes.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<ax::mojom::AXLocationChangesDataView,
+                    ui::AXLocationChanges> {
+  static int32_t id(const ui::AXLocationChanges& p) { return p.id; }
+  static const ui::AXTreeID& ax_tree_id(const ui::AXLocationChanges& p) {
+    return p.ax_tree_id;
+  }
+  static const ui::AXRelativeBounds& new_location(
+      const ui::AXLocationChanges& p) {
+    return p.new_location;
+  }
+  static bool Read(ax::mojom::AXLocationChangesDataView data,
+                   ui::AXLocationChanges* out);
+};
+
+}  // namespace mojo
+
+#endif  // UI_ACCESSIBILITY_MOJOM_AX_LOCATION_CHANGES_MOJOM_TRAITS_H_
diff --git a/ui/accessibility/mojom/ax_location_changes_mojom_traits_unittest.cc b/ui/accessibility/mojom/ax_location_changes_mojom_traits_unittest.cc
new file mode 100644
index 0000000..52209b2
--- /dev/null
+++ b/ui/accessibility/mojom/ax_location_changes_mojom_traits_unittest.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 "ui/accessibility/mojom/ax_location_changes_mojom_traits.h"
+
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/ax_updates_and_events.h"
+#include "ui/accessibility/mojom/ax_location_changes.mojom.h"
+
+using mojo::test::SerializeAndDeserialize;
+
+TEST(AXLocationChangesMojomTraitsTest, RoundTrip) {
+  ui::AXLocationChanges input;
+  input.id = 1;
+  ui::AXTreeID input_tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  input.ax_tree_id = input_tree_id;
+
+  ui::AXRelativeBounds input_bounds;
+  input_bounds.offset_container_id = 111;
+  input_bounds.bounds = gfx::RectF(1, 2, 3, 4);
+  input_bounds.transform = std::make_unique<gfx::Transform>();
+  input_bounds.transform->Scale(1.0, 2.0);
+  input.new_location = input_bounds;
+
+  ui::AXLocationChanges output;
+  EXPECT_TRUE(
+      SerializeAndDeserialize<ax::mojom::AXLocationChanges>(input, output));
+  EXPECT_EQ(1, output.id);
+  EXPECT_EQ(input_tree_id, output.ax_tree_id);
+  EXPECT_EQ(111, output.new_location.offset_container_id);
+  EXPECT_EQ(1, output.new_location.bounds.x());
+  EXPECT_EQ(2, output.new_location.bounds.y());
+  EXPECT_EQ(3, output.new_location.bounds.width());
+  EXPECT_EQ(4, output.new_location.bounds.height());
+  EXPECT_FALSE(output.new_location.transform->IsIdentity());
+}
diff --git a/ui/accessibility/platform/browser_accessibility_manager.cc b/ui/accessibility/platform/browser_accessibility_manager.cc
index 7b86824..b72b87a 100644
--- a/ui/accessibility/platform/browser_accessibility_manager.cc
+++ b/ui/accessibility/platform/browser_accessibility_manager.cc
@@ -1905,18 +1905,6 @@
   if (node->GetRole() == ax::mojom::Role::kInlineTextBox)
     return false;
 
-  // Don't fire events when the node has the role of "alert" and has no
-  // text content.
-  if (node->GetRole() == ax::mojom::Role::kAlert) {
-    std::u16string node_text = node->GetTextContentUTF16();
-
-    // The string is empty or contains only whitespace characters
-    if (node_text.empty() ||
-        base::ContainsOnlyChars(node_text, base::kWhitespaceUTF16)) {
-      return false;
-    }
-  }
-
   return true;
 }
 
diff --git a/ui/accessibility/platform/browser_accessibility_manager.h b/ui/accessibility/platform/browser_accessibility_manager.h
index 3dfba7ba..37cad86 100644
--- a/ui/accessibility/platform/browser_accessibility_manager.h
+++ b/ui/accessibility/platform/browser_accessibility_manager.h
@@ -502,10 +502,6 @@
  protected:
   FRIEND_TEST_ALL_PREFIXES(content::BrowserAccessibilityManagerTest,
                            TestShouldFireEventForNode);
-  FRIEND_TEST_ALL_PREFIXES(content::BrowserAccessibilityManagerTest,
-                           TestShouldFireEventForAlertEventWithEmptyName);
-  FRIEND_TEST_ALL_PREFIXES(content::BrowserAccessibilityManagerTest,
-                           TestShouldFireEventForAlertEventWithNonEmptyName);
 
   explicit BrowserAccessibilityManager(AXNodeIdDelegate& node_id_delegate,
                                        AXPlatformTreeManagerDelegate* delegate);
diff --git a/ui/accessibility/platform/browser_accessibility_manager_win.cc b/ui/accessibility/platform/browser_accessibility_manager_win.cc
index e17d0c88..472e37b8 100644
--- a/ui/accessibility/platform/browser_accessibility_manager_win.cc
+++ b/ui/accessibility/platform/browser_accessibility_manager_win.cc
@@ -259,7 +259,10 @@
       break;
     case AXEventGenerator::Event::ALERT:
       FireWinAccessibilityEvent(EVENT_SYSTEM_ALERT, wrapper);
-      FireUiaAccessibilityEvent(UIA_SystemAlertEventId, wrapper);
+      // Generated 'ALERT' events come from role=alert nodes in the tree.
+      // These should just be treated as normal live region changed events,
+      // since we don't want web pages to be performing system-wide alerts.
+      FireUiaAccessibilityEvent(UIA_LiveRegionChangedEventId, wrapper);
       break;
     case AXEventGenerator::Event::ATOMIC_CHANGED:
       HandleAriaPropertiesChangedEvent(*wrapper);
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 51c8ad1..9f34c4d 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -303,6 +303,7 @@
     "java/src/org/chromium/ui/base/Clipboard.java",
     "java/src/org/chromium/ui/base/ClipboardImpl.java",
     "java/src/org/chromium/ui/base/DeviceFormFactor.java",
+    "java/src/org/chromium/ui/base/DeviceInput.java",
     "java/src/org/chromium/ui/base/EventForwarder.java",
     "java/src/org/chromium/ui/base/EventOffsetHandler.java",
     "java/src/org/chromium/ui/base/IdleDetector.java",
@@ -590,6 +591,7 @@
     "junit/src/org/chromium/ui/base/ActivityKeyboardVisibilityDelegateUnitTest.java",
     "junit/src/org/chromium/ui/base/ApplicationViewportInsetSupplierTest.java",
     "junit/src/org/chromium/ui/base/ClipboardTest.java",
+    "junit/src/org/chromium/ui/base/DeviceInputTest.java",
     "junit/src/org/chromium/ui/base/EventForwarderTest.java",
     "junit/src/org/chromium/ui/base/EventOffsetHandlerTest.java",
     "junit/src/org/chromium/ui/base/LocalizationUtilsTest.java",
diff --git a/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java b/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java
index d13ce814..bfce8b8 100644
--- a/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java
+++ b/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java
@@ -12,6 +12,7 @@
 import org.jni_zero.CalledByNative;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.ResettersForTesting;
 import org.chromium.base.ThreadUtils;
 import org.chromium.ui.R;
 import org.chromium.ui.display.DisplayAndroid;
@@ -20,8 +21,8 @@
 /** UI utilities for accessing form factor information. */
 public class DeviceFormFactor {
     /**
-     * Minimum screen size in dp to be considered a tablet. Matches the value
-     * used by res/ directories. E.g.: res/values-sw600dp/values.xml
+     * Minimum screen size in dp to be considered a tablet. Matches the value used by res/
+     * directories. E.g.: res/values-sw600dp/values.xml
      */
     public static final int MINIMUM_TABLET_WIDTH_DP = 600;
 
@@ -31,29 +32,45 @@
     /** Matches the value set in res/values-sw720dp/values.xml */
     private static final int SCREEN_BUCKET_LARGET_TABLET = 3;
 
+    /** See {@link #setIsTabletForTesting(boolean)}. */
+    private static Boolean sIsTabletForTesting;
+
     /**
      * Each activity could be on a different display, and this will just tell you whether the
-     * display associated with the application context is "tablet sized".
-     * Use {@link #isNonMultiDisplayContextOnTablet} or {@link #isWindowOnTablet} instead.
+     * display associated with the application context is "tablet sized". Use {@link
+     * #isNonMultiDisplayContextOnTablet} or {@link #isWindowOnTablet} instead.
      */
     @CalledByNative
     @Deprecated
     public static boolean isTablet() {
+        if (sIsTabletForTesting != null) {
+            return sIsTabletForTesting;
+        }
         return detectScreenWidthBucket(ContextUtils.getApplicationContext())
                 >= SCREEN_BUCKET_TABLET;
     }
 
     /**
-     * See {@link DisplayAndroid#getNonMultiDisplay}} for what "NonMultiDisplay" means.
-     * When possible, it is generally more correct to use {@link #isWindowOnTablet}.
-     * Only Activity instances and Contexts that wrap Activities are meaningfully associated with
-     * displays, so care should be taken to pass a context that makes sense.
+     * Modifies the output of {@link #isTablet()} for testing. Note that it is preferable to use
+     * {@link org.robolectric.annotation.Config} annotations to specify screen dimensions when
+     * possible. This method exists for instances where it is not possible or where it is cumbersome
+     * to do so, e.g. when device form factor is parameterized in a test suite.
+     */
+    public static void setIsTabletForTesting(Boolean isTablet) {
+        sIsTabletForTesting = isTablet;
+        ResettersForTesting.register(() -> sIsTabletForTesting = null);
+    }
+
+    /**
+     * See {@link DisplayAndroid#getNonMultiDisplay}} for what "NonMultiDisplay" means. When
+     * possible, it is generally more correct to use {@link #isWindowOnTablet}. Only Activity
+     * instances and Contexts that wrap Activities are meaningfully associated with displays, so
+     * care should be taken to pass a context that makes sense.
      *
      * @return Whether the display associated with the given context is large enough to be
-     *         considered a tablet and will thus load tablet-specific resources (those in the config
-     *         -sw600).
-     *         Not affected by Android N multi-window, but can change for external displays.
-     *         E.g. http://developer.samsung.com/samsung-dex/testing
+     *     considered a tablet and will thus load tablet-specific resources (those in the config
+     *     -sw600). Not affected by Android N multi-window, but can change for external displays.
+     *     E.g. http://developer.samsung.com/samsung-dex/testing
      */
     public static boolean isNonMultiDisplayContextOnTablet(Context context) {
         return detectScreenWidthBucket(context) >= SCREEN_BUCKET_TABLET;
diff --git a/ui/android/java/src/org/chromium/ui/base/DeviceInput.java b/ui/android/java/src/org/chromium/ui/base/DeviceInput.java
new file mode 100644
index 0000000..0ca9e28
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/base/DeviceInput.java
@@ -0,0 +1,172 @@
+// 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.ui.base;
+
+import static android.view.InputDevice.KEYBOARD_TYPE_ALPHABETIC;
+import static android.view.InputDevice.SOURCE_MOUSE;
+
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.hardware.input.InputManager.InputDeviceListener;
+import android.util.SparseArray;
+import android.view.InputDevice;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.ResettersForTesting;
+import org.chromium.base.ThreadUtils;
+
+/**
+ * Utilities for accessing device input information. Note that this class is not thread-safe and
+ * currently asserts all interactions occur on the UI thread. If usage is required off the UI thread
+ * in the future, this class can be modified for multi-thread support.
+ */
+public class DeviceInput implements InputDeviceListener {
+
+    /** Wrapper class which provides lazy initialization of a singleton instance. */
+    private static final class LazyInit {
+        public static final DeviceInput sInstance = new DeviceInput();
+    }
+
+    /** See {@link #setSupportsAlphabeticKeyboardForTesting(boolean)}. */
+    private static Boolean sSupportsAlphabeticKeyboardForTesting;
+
+    /** See {@link #setSupportsPrevisionPointerForTesting(boolean)}. */
+    private static Boolean sSupportsPrecisionPointerForTesting;
+
+    /** Cached snapshots of all currently connected {@link InputDevice}s. */
+    private final SparseArray<DeviceSnapshot> mDeviceSnapshotsById = new SparseArray<>();
+
+    /** Only a lazy singleton instance may be instantiated. */
+    private DeviceInput() {
+        ThreadUtils.assertOnUiThread();
+
+        // Initialize cache.
+        final int[] deviceIds = InputDevice.getDeviceIds();
+        for (int i = 0; i < deviceIds.length; i++) {
+            int deviceId = deviceIds[i];
+            var snapshot = DeviceSnapshot.from(InputDevice.getDevice(deviceId));
+            mDeviceSnapshotsById.put(deviceId, snapshot);
+        }
+
+        // Register listener to perform cache updates.
+        var context = ContextUtils.getApplicationContext();
+        var inputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+        inputManager.registerInputDeviceListener(this, /* handler= */ null);
+    }
+
+    /** Returns a lazily instantiated singleton instance. */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static DeviceInput getInstance() {
+        ThreadUtils.assertOnUiThread();
+        return LazyInit.sInstance;
+    }
+
+    /** Modifies the output of {@link #supportsAlphabeticKeyboard()} for testing. */
+    public static void setSupportsAlphabeticKeyboardForTesting(Boolean supportsAlphabeticKeyboard) {
+        sSupportsAlphabeticKeyboardForTesting = supportsAlphabeticKeyboard;
+        ResettersForTesting.register(() -> sSupportsAlphabeticKeyboardForTesting = null);
+    }
+
+    /**
+     * @return Whether any currently connected {@link InputDevice} supports an alphabetic keyboard.
+     */
+    public static boolean supportsAlphabeticKeyboard() {
+        ThreadUtils.assertOnUiThread();
+        return getInstance().supportsAlphabeticKeyboardImpl();
+    }
+
+    /** Implementation of {@link #supportsAlphabeticKeyboard()}. */
+    public boolean supportsAlphabeticKeyboardImpl() {
+        ThreadUtils.assertOnUiThread();
+        if (sSupportsAlphabeticKeyboardForTesting != null) {
+            return sSupportsAlphabeticKeyboardForTesting;
+        }
+        for (int i = 0; i < mDeviceSnapshotsById.size(); i++) {
+            if (mDeviceSnapshotsById.valueAt(i).supportsAlphabeticKeyboard) return true;
+        }
+        return false;
+    }
+
+    /** Modifies the output of {@link #supportsPrecisionPointer()} for testing. */
+    public static void setSupportsPrecisionPointerForTesting(Boolean supportsPrecisionPointer) {
+        sSupportsPrecisionPointerForTesting = supportsPrecisionPointer;
+        ResettersForTesting.register(() -> sSupportsPrecisionPointerForTesting = null);
+    }
+
+    /**
+     * @return Whether any currently connected {@link InputDevice} supports precision pointing. Note
+     *     that this includes not only mice, but also any mice-like pointing devices (e.g. stylus,
+     *     touchpad, etc).
+     */
+    public static boolean supportsPrecisionPointer() {
+        ThreadUtils.assertOnUiThread();
+        return getInstance().supportsPrecisionPointerImpl();
+    }
+
+    /** Implementation of {@link #supportsPrecisionPointer()}. */
+    private boolean supportsPrecisionPointerImpl() {
+        ThreadUtils.assertOnUiThread();
+        if (sSupportsPrecisionPointerForTesting != null) {
+            return sSupportsPrecisionPointerForTesting;
+        }
+        for (int i = 0; i < mDeviceSnapshotsById.size(); i++) {
+            if (mDeviceSnapshotsById.valueAt(i).supportsPrecisionPointer) return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onInputDeviceAdded(int deviceId) {
+        ThreadUtils.assertOnUiThread();
+        mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(InputDevice.getDevice(deviceId)));
+    }
+
+    @Override
+    public void onInputDeviceChanged(int deviceId) {
+        ThreadUtils.assertOnUiThread();
+        mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(InputDevice.getDevice(deviceId)));
+    }
+
+    @Override
+    public void onInputDeviceRemoved(int deviceId) {
+        ThreadUtils.assertOnUiThread();
+        mDeviceSnapshotsById.remove(deviceId);
+    }
+
+    /** Class which represents a snapshot of given {@link InputDevice}. */
+    private static final class DeviceSnapshot {
+
+        /** Whether the associated {@link InputDevice} supports an alphabetic keyboard. */
+        public final boolean supportsAlphabeticKeyboard;
+
+        /**
+         * Whether the associated {@link InputDevice} supports precision pointing. Note that this
+         * includes not only mice, but also any mice-like pointing devices (e.g. stylus, touchpad,
+         * etc).
+         */
+        public final boolean supportsPrecisionPointer;
+
+        /** See {@link #from(InputDevice)}. */
+        private DeviceSnapshot(
+                boolean supportsAlphabeticKeyboard, boolean supportsPrecisionPointer) {
+            this.supportsAlphabeticKeyboard = supportsAlphabeticKeyboard;
+            this.supportsPrecisionPointer = supportsPrecisionPointer;
+        }
+
+        /**
+         * @return a new snapshot representing the specified {@link InputDevice}.
+         */
+        public static DeviceSnapshot from(InputDevice device) {
+            boolean isPhysical = !device.isVirtual();
+            return new DeviceSnapshot(
+                    /* supportsAlphabeticKeyboard= */ isPhysical
+                            && device.getKeyboardType() == KEYBOARD_TYPE_ALPHABETIC,
+                    /* supportsPrecisionPointer= */ isPhysical
+                            && device.supportsSource(SOURCE_MOUSE));
+        }
+    }
+}
diff --git a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
index a4bf8f7..b40ec7af 100644
--- a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
+++ b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java
@@ -339,6 +339,15 @@
         mAllowMultiple = multiple;
         mWindowAndroid = (sWindowAndroidForTesting == null) ? window : sWindowAndroidForTesting;
 
+        // No mime types or extra choosers needed for open-directory.
+        if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(mIntentAction)) {
+            Intent intent = new Intent(mIntentAction);
+            if (!mWindowAndroid.showIntent(intent, this, R.string.low_memory_error)) {
+                onFileNotSelected();
+            }
+            return;
+        }
+
         mSupportsImageCapture =
                 mWindowAndroid.canResolveActivity(new Intent(MediaStore.ACTION_IMAGE_CAPTURE));
         mSupportsVideoCapture =
@@ -560,10 +569,8 @@
             getContentIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
         }
 
-        // Set to all types if not a dir, and restrict further by MIME-type below.
-        if (!Intent.ACTION_OPEN_DOCUMENT_TREE.equals(getContentIntent.getAction())) {
-            getContentIntent.setType(ALL_TYPES);
-        }
+        // Set to all types, and restrict further by MIME-type below.
+        getContentIntent.setType(ALL_TYPES);
 
         if (mMimeTypes.size() > 0) {
             // If some of the extensions are generic, just let user selectall files.
@@ -623,10 +630,8 @@
             getContentIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
         }
 
-        // Set to all types if not a dir, but potentially restricted further by MIME-type below.
-        if (!Intent.ACTION_OPEN_DOCUMENT_TREE.equals(getContentIntent.getAction())) {
-            getContentIntent.setType(ALL_TYPES);
-        }
+        // Set to all types, but potentially restricted further by MIME-type below.
+        getContentIntent.setType(ALL_TYPES);
 
         ArrayList<Intent> extraIntents = new ArrayList<Intent>();
         if (acceptsSingleType()) {
diff --git a/ui/android/junit/src/org/chromium/ui/base/DeviceInputTest.java b/ui/android/junit/src/org/chromium/ui/base/DeviceInputTest.java
new file mode 100644
index 0000000..a06acac
--- /dev/null
+++ b/ui/android/junit/src/org/chromium/ui/base/DeviceInputTest.java
@@ -0,0 +1,211 @@
+// 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.ui.base;
+
+import static android.view.InputDevice.KEYBOARD_TYPE_ALPHABETIC;
+import static android.view.InputDevice.KEYBOARD_TYPE_NONE;
+import static android.view.InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC;
+import static android.view.InputDevice.SOURCE_KEYBOARD;
+import static android.view.InputDevice.SOURCE_MOUSE;
+
+import android.util.SparseArray;
+import android.view.InputDevice;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+import org.robolectric.shadow.api.Shadow;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Tests for {@link DeviceInput}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(shadows = {DeviceInputTest.ShadowInputDevice.class})
+public class DeviceInputTest {
+
+    @After
+    public void tearDown() {
+        ShadowInputDevice.reset();
+    }
+
+    @Test
+    @SmallTest
+    public void testSupportsAlphabeticKeyboard() {
+        int nextDeviceId = 1;
+
+        // Attach non-keyboard.
+        ShadowInputDevice.attach(nextDeviceId++)
+                .setKeyboardType(KEYBOARD_TYPE_NONE)
+                .setSources(SOURCE_MOUSE);
+
+        // Attach non-alphabetic keyboard.
+        ShadowInputDevice.attach(nextDeviceId++)
+                .setKeyboardType(KEYBOARD_TYPE_NON_ALPHABETIC)
+                .setSources(SOURCE_KEYBOARD);
+
+        // Attach non-physical alphabetic keyboard.
+        ShadowInputDevice alphabeticKeyboard =
+                ShadowInputDevice.attach(nextDeviceId++)
+                        .setIsVirtual(true)
+                        .setKeyboardType(KEYBOARD_TYPE_ALPHABETIC)
+                        .setSources(SOURCE_KEYBOARD);
+
+        // Case: No physical alphabetic keyboards attached.
+        Assert.assertFalse(DeviceInput.supportsAlphabeticKeyboard());
+
+        // Case: A non-physical alphabetic keyboard was updated to become physical (contrived).
+        alphabeticKeyboard.setIsVirtual(false);
+        Assert.assertTrue(DeviceInput.supportsAlphabeticKeyboard());
+
+        // Case: A physical alphabetic keyboard was detached.
+        ShadowInputDevice.detach(alphabeticKeyboard.getId());
+        Assert.assertFalse(DeviceInput.supportsAlphabeticKeyboard());
+
+        // Case: A physical alphabetic keyboard was attached.
+        ShadowInputDevice.attach(nextDeviceId++)
+                .setKeyboardType(KEYBOARD_TYPE_ALPHABETIC)
+                .setSources(SOURCE_KEYBOARD);
+        Assert.assertTrue(DeviceInput.supportsAlphabeticKeyboard());
+    }
+
+    @Test
+    @SmallTest
+    public void testSupportsPrecisionPointer() {
+        int nextDeviceId = 1;
+
+        // Attach non-precision pointer.
+        ShadowInputDevice.attach(nextDeviceId++)
+                .setKeyboardType(KEYBOARD_TYPE_ALPHABETIC)
+                .setSources(SOURCE_KEYBOARD);
+
+        // Attach precision pointer.
+        ShadowInputDevice precisionPointer =
+                ShadowInputDevice.attach(nextDeviceId++)
+                        .setIsVirtual(true)
+                        .setSources(SOURCE_MOUSE);
+
+        // Case: No physical precision pointer attached.
+        Assert.assertFalse(DeviceInput.supportsPrecisionPointer());
+
+        // Case: A non-physical precision pointer was updated to become physical (contrived).
+        precisionPointer.setIsVirtual(false);
+        Assert.assertTrue(DeviceInput.supportsPrecisionPointer());
+
+        // Case: A physical precision pointer was detached.
+        ShadowInputDevice.detach(precisionPointer.getId());
+        Assert.assertFalse(DeviceInput.supportsPrecisionPointer());
+
+        // Case: A physical precision pointer was attached.
+        ShadowInputDevice.attach(nextDeviceId++).setSources(SOURCE_MOUSE);
+        Assert.assertTrue(DeviceInput.supportsPrecisionPointer());
+    }
+
+    @Implements(InputDevice.class)
+    public static class ShadowInputDevice extends org.robolectric.shadows.ShadowInputDevice {
+
+        private static final SparseArray<InputDevice> sDevicesById = new SparseArray<>();
+
+        private int mId;
+        private boolean mIsVirtual;
+        private int mKeyboardType;
+        private int mSources;
+
+        public static ShadowInputDevice attach(int deviceId) {
+            assert sDevicesById.indexOfKey(deviceId) < 0;
+
+            InputDevice device = Shadow.newInstanceOf(InputDevice.class);
+            sDevicesById.put(deviceId, device);
+
+            ShadowInputDevice shadow = Shadow.extract(device);
+            shadow.mId = deviceId;
+
+            DeviceInput.getInstance().onInputDeviceAdded(deviceId);
+
+            return shadow;
+        }
+
+        public static void detach(int deviceId) {
+            sDevicesById.remove(deviceId);
+            DeviceInput.getInstance().onInputDeviceRemoved(deviceId);
+        }
+
+        @Resetter
+        public static void reset() {
+            for (int i = sDevicesById.size() - 1; i >= 0; i--) {
+                detach(sDevicesById.keyAt(i));
+            }
+        }
+
+        @Implementation
+        public static InputDevice getDevice(int deviceId) {
+            return sDevicesById.get(deviceId);
+        }
+
+        @Implementation
+        public static int[] getDeviceIds() {
+            int[] deviceIds = new int[sDevicesById.size()];
+            for (int i = 0; i < sDevicesById.size(); i++) {
+                deviceIds[i] = sDevicesById.keyAt(i);
+            }
+            return deviceIds;
+        }
+
+        @Implementation
+        public int getId() {
+            return mId;
+        }
+
+        @Implementation
+        public int getKeyboardType() {
+            return mKeyboardType;
+        }
+
+        public ShadowInputDevice setKeyboardType(int keyboardType) {
+            if (mKeyboardType != keyboardType) {
+                mKeyboardType = keyboardType;
+                DeviceInput.getInstance().onInputDeviceChanged(mId);
+            }
+            return this;
+        }
+
+        @Implementation
+        public int getSources() {
+            return mSources;
+        }
+
+        public ShadowInputDevice setSources(int sources) {
+            if (mSources != sources) {
+                mSources = sources;
+                DeviceInput.getInstance().onInputDeviceChanged(mId);
+            }
+            return this;
+        }
+
+        @Implementation
+        public boolean isVirtual() {
+            return mIsVirtual;
+        }
+
+        public ShadowInputDevice setIsVirtual(boolean isVirtual) {
+            if (mIsVirtual != isVirtual) {
+                mIsVirtual = isVirtual;
+                DeviceInput.getInstance().onInputDeviceChanged(mId);
+            }
+            return this;
+        }
+
+        @Implementation
+        public boolean supportsSource(int source) {
+            return (getSources() & source) == source;
+        }
+    }
+}
diff --git a/ui/android/junit/src/org/chromium/ui/base/SelectFileDialogTest.java b/ui/android/junit/src/org/chromium/ui/base/SelectFileDialogTest.java
index 9f7975d05..33a6e0f 100644
--- a/ui/android/junit/src/org/chromium/ui/base/SelectFileDialogTest.java
+++ b/ui/android/junit/src/org/chromium/ui/base/SelectFileDialogTest.java
@@ -269,6 +269,43 @@
     }
 
     @Test
+    @EnableFeatures({UiAndroidFeatures.SELECT_FILE_OPEN_DOCUMENT})
+    public void testMimeTypesWithExternalPickerOpenDocumentTree() throws Exception {
+        TestSelectFileDialog selectFileDialog = new TestSelectFileDialog(0);
+        WindowAndroid windowAndroid = Mockito.mock(WindowAndroid.class);
+
+        // Setup WindowAndroid#showIntent to succeed (and validate the call).
+        IntentArgumentMatcher chooserIntentArgumentMatcher =
+                new IntentArgumentMatcher(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE));
+        Mockito.doAnswer(
+                        (invocation) -> {
+                            // Validate open-dir intent has no extra choosers, mimes, etc.
+                            Intent intent = (Intent) invocation.getArguments()[0];
+                            assertEquals(null, intent.getExtra(Intent.EXTRA_INTENT));
+                            assertEquals(null, intent.getType());
+                            assertEquals(null, intent.getExtra(Intent.EXTRA_MIME_TYPES));
+                            assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
+                            return true;
+                        })
+                .when(windowAndroid)
+                .showIntent(
+                        ArgumentMatchers.argThat(chooserIntentArgumentMatcher),
+                        (WindowAndroid.IntentCallback) any(),
+                        anyInt());
+
+        // Simulate showing the dialog, allowing a directory to be selected.
+        selectFileDialog.selectFile(
+                Intent.ACTION_OPEN_DOCUMENT_TREE,
+                /* fileTypes= */ new String[] {},
+                /* capture= */ false,
+                /* multiple= */ false,
+                windowAndroid);
+        assertEquals(0, selectFileDialog.mFileSelectionSuccess);
+        assertEquals(0, selectFileDialog.mFileSelectionAborted);
+        selectFileDialog.resetFileSelectionAttempts();
+    }
+
+    @Test
     public void testMimeTypesWithExternalPickerNoAcceptList() throws Exception {
         TestSelectFileDialog selectFileDialog = new TestSelectFileDialog(0);
         WindowAndroid windowAndroid = Mockito.mock(WindowAndroid.class);
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 64aca011..79937be 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -123,6 +123,10 @@
   if (!is_ios) {
     sources += [ "ui_base_types.h" ]
   }
+
+  # Need to be removed once all clients of `MenuSourceType` have been migrated.
+  public_deps = [ "//ui/base/mojom:ui_base_types" ]
+
   deps = [ "//build:chromeos_buildflags" ]
 }
 
@@ -538,6 +542,7 @@
   if (use_blink) {
     public_deps += [
       "//ui/base/clipboard:file_info",
+      "//ui/base/mojom:ui_base_types",
       "//ui/events:dom_keycode_converter",
       "//ui/events:events_base",
       "//ui/events/platform",
@@ -865,6 +870,11 @@
     ":base",
     "//ui/base/idle",
   ]
+
+  if (!is_ios) {
+    public_deps += [ "//ui/base/mojom:ui_base_types" ]
+  }
+
   deps = [
     "//base",
     "//base/test:test_config",
diff --git a/ui/base/base_window.h b/ui/base/base_window.h
index 05fea4c5..f3f6a04 100644
--- a/ui/base/base_window.h
+++ b/ui/base/base_window.h
@@ -7,6 +7,7 @@
 
 #include "base/component_export.h"
 #include "build/build_config.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -44,7 +45,7 @@
   virtual gfx::Rect GetRestoredBounds() const = 0;
 
   // Returns the restore state for the window (platform dependent).
-  virtual ui::WindowShowState GetRestoredState() const = 0;
+  virtual ui::mojom::WindowShowState GetRestoredState() const = 0;
 
   // Retrieves the window's current bounds, including its window.
   // This will only differ from GetRestoredBounds() for maximized
diff --git a/ui/base/cocoa/menu_utils.mm b/ui/base/cocoa/menu_utils.mm
index 2352d38..401ca1d 100644
--- a/ui/base/cocoa/menu_utils.mm
+++ b/ui/base/cocoa/menu_utils.mm
@@ -83,7 +83,25 @@
   [NSMenu popUpContextMenu:menu withEvent:event forView:view];
 
   if (context) {
-    ui::ElementTrackerMac::GetInstance()->NotifyMenuDoneShowing(menu);
+    // We expect to see the following order of events:
+    //
+    // - menu will show
+    // - element shown
+    // - element activated (optional)
+    // - element hidden
+    // - menu completed
+    //
+    // However, the OS notification for "element activated" fires *after* the OS
+    // notification for "element hidden", so Chromium code handling the "element
+    // hidden" callback responds by doing a post to the main dispatch queue.
+    // Therefore, because there's already a post on the main dispatch queue,
+    // this event must be posted to the main dispatch queue as well to ensure
+    // correct ordering.
+    dispatch_after(
+        dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC),
+        dispatch_get_main_queue(), ^{
+          ui::ElementTrackerMac::GetInstance()->NotifyMenuDoneShowing(menu);
+        });
   }
 }
 
diff --git a/ui/base/interaction/OWNERS b/ui/base/interaction/OWNERS
index f8cdc787..089b89f 100644
--- a/ui/base/interaction/OWNERS
+++ b/ui/base/interaction/OWNERS
@@ -1,2 +1,4 @@
 dfried@chromium.org
 collinbaker@chromium.org
+
+per-file element_tracker_mac*=avi@chromium.org
diff --git a/ui/base/interaction/element_tracker_mac.mm b/ui/base/interaction/element_tracker_mac.mm
index 76de355..66c2277 100644
--- a/ui/base/interaction/element_tracker_mac.mm
+++ b/ui/base/interaction/element_tracker_mac.mm
@@ -12,6 +12,12 @@
 #include "base/no_destructor.h"
 #include "ui/base/interaction/element_identifier.h"
 
+// Note the variation in logging used in this file. For assertions about values
+// passed within Chromium, CHECK is used, but for callbacks derived from OS
+// notifications, LOG(ERROR) is used, as hard failure is not desired for
+// something out of Chromium's control, but a very noisy failure is desired so
+// that it can be noticed and fixed.
+
 namespace ui {
 
 DEFINE_FRAMEWORK_SPECIFIC_METADATA(TrackedElementMac)
@@ -32,14 +38,16 @@
 class ElementTrackerMac::MenuData {
  public:
   explicit MenuData(ElementContext context) : context_(context) {
-    DCHECK(context);
+    CHECK(context);
   }
 
   ~MenuData() {
-    HideElements(recycle_bin_);
-    LOG_IF(WARNING, !elements_.empty())
+    LOG_IF(ERROR, !elements_.empty())
         << "Destroying menu data before all elements are hidden.";
-    HideElements(elements_);
+    for (auto& [identifier, element] : elements_) {
+      ui::ElementTracker::GetFrameworkDelegate()->NotifyElementHidden(
+          element.get());
+    }
   }
 
   MenuData(const MenuData& other) = delete;
@@ -50,86 +58,41 @@
   // Adds an element representing a menu item. The item must not already exist.
   void AddElement(ElementIdentifier identifier,
                   const gfx::Rect& screen_bounds) {
-    // On show of a new menu or submenu, clear out any menu items waiting to be
-    // garbage-collected, since an activation won't happen.
-    HideElements(recycle_bin_);
-
-    // Insert an entry for the new element and verify that it is correct.
     const auto result =
         elements_.emplace(identifier, std::make_unique<TrackedElementMac>(
                                           identifier, context_, screen_bounds));
-    DCHECK(result.second);
+    LOG_IF(ERROR, !result.second) << "Element " << identifier << " added twice";
     ui::ElementTracker::GetFrameworkDelegate()->NotifyElementShown(
         result.first->second.get());
   }
 
-  // Notifies that the specified element has been activated. Also checks
-  // elements in the recycle bin, in case hide and activate came arrived in the
-  // wrong order.
+  // Notifies that the specified element has been activated.
   void ActivateElement(ElementIdentifier identifier) {
-    auto it = elements_.find(identifier);
+    const auto it = elements_.find(identifier);
     if (it == elements_.end()) {
-      it = recycle_bin_.find(identifier);
-      if (it == recycle_bin_.end()) {
-        DUMP_WILL_BE_NOTREACHED()
-            << "Element " << identifier
-            << " had its activation sent after its menu was destroyed. This "
-               "may be due to a race condition with renderer context menus; "
-               "see crbug.com/1418614 for the state of current efforts to "
-               "diagnose and fix the problem.";
-        return;
-      }
-      LOG(WARNING) << "Element " << identifier
-                   << " activated after hide signal received.";
+      LOG(ERROR) << "Element " << identifier << " activated after being hidden";
     }
     ui::ElementTracker::GetFrameworkDelegate()->NotifyElementActivated(
         it->second.get());
   }
 
-  // Notifies that the element with `identifier` was hidden. Instead of deleting
-  // the element right away, it is instead put in the `recycle_bin_` for
-  // disposal when it is certain no activation event will occur.
+  // Notifies that the element with `identifier` was hidden.
   void HideElement(ElementIdentifier identifier) {
     const auto it = elements_.find(identifier);
     if (it == elements_.end()) {
-      CHECK(base::Contains(recycle_bin_, identifier))
-          << "Element " << identifier
-          << " hidden after its menu was destroyed.";
-
-      LOG(WARNING) << "Element " << identifier
-                   << " hidden multiple times in a row.";
+      LOG(ERROR) << "Element " << identifier << " hidden multiple times";
       return;
     }
-    auto result = recycle_bin_.emplace(identifier, std::move(it->second));
-    DCHECK(result.second);
+    ui::ElementTracker::GetFrameworkDelegate()->NotifyElementHidden(
+        it->second.get());
     elements_.erase(it);
   }
 
  private:
-  using ElementMap =
-      std::map<ElementIdentifier, std::unique_ptr<TrackedElementMac>>;
-
-  // Hides all of the elements in `map` and then clears the map. Used to do
-  // final disposal of TrackedElementMac objects owned by this object.
-  void HideElements(ElementMap& map) {
-    for (auto& [identifier, element] : map) {
-      ui::ElementTracker::GetFrameworkDelegate()->NotifyElementHidden(
-          element.get());
-    }
-    map.clear();
-  }
-
   const ElementContext context_;
 
-  // Keeps track of all "live" elements being tracked by this object. When they
-  // are hidden, they are moved to `recycle_bin_` to await final disposal.
-  ElementMap elements_;
-
-  // Because activation and hide events can come in reverse order in some corner
-  // cases (see crbug.com/1432480) elements are kept around between the time
-  // they are notified as hidden and when they are actually destroyed, which is
-  // when the menu is destroyed or when a new submenu opens.
-  ElementMap recycle_bin_;
+  // Keeps track of all "live" elements being tracked by this object.
+  std::map<ElementIdentifier, std::unique_ptr<TrackedElementMac>> elements_;
 };
 
 // static
@@ -141,12 +104,12 @@
 void ElementTrackerMac::NotifyMenuWillShow(NSMenu* menu,
                                            ElementContext context) {
   const auto result = root_menu_to_data_.emplace(menu, context);
-  DCHECK(result.second);
+  LOG_IF(ERROR, !result.second) << "Menu added twice";
 }
 
 void ElementTrackerMac::NotifyMenuDoneShowing(NSMenu* menu) {
   const auto result = root_menu_to_data_.erase(menu);
-  DCHECK(result);
+  LOG_IF(ERROR, !result) << "Menu removed twice";
 }
 
 void ElementTrackerMac::NotifyMenuItemShown(NSMenu* menu,
@@ -155,6 +118,8 @@
   const auto it = root_menu_to_data_.find(GetRootMenu(menu));
   if (it != root_menu_to_data_.end()) {
     it->second.AddElement(identifier, screen_bounds);
+  } else {
+    LOG(ERROR) << "Element " << identifier << " shown with unknown menu";
   }
 }
 
@@ -163,6 +128,8 @@
   const auto it = root_menu_to_data_.find(GetRootMenu(menu));
   if (it != root_menu_to_data_.end()) {
     it->second.ActivateElement(identifier);
+  } else {
+    LOG(ERROR) << "Element " << identifier << " activated with unknown menu";
   }
 }
 
@@ -171,6 +138,8 @@
   const auto it = root_menu_to_data_.find(GetRootMenu(menu));
   if (it != root_menu_to_data_.end()) {
     it->second.HideElement(identifier);
+  } else {
+    LOG(ERROR) << "Element " << identifier << " hidden with unknown menu";
   }
 }
 
diff --git a/ui/base/interaction/element_tracker_mac_unittest.mm b/ui/base/interaction/element_tracker_mac_unittest.mm
index bf3008b..e232d50f 100644
--- a/ui/base/interaction/element_tracker_mac_unittest.mm
+++ b/ui/base/interaction/element_tracker_mac_unittest.mm
@@ -475,49 +475,4 @@
   element_tracker_->NotifyMenuDoneShowing(kFakeMenu1);
 }
 
-// The following tests are regression tests for the likely cause of
-// crbug.com/1432480.
-
-TEST_F(ElementTrackerMacTest, TrackerIsRobustToOutOfOrderActivation) {
-  ExpectedCall shown(kElementIdentifier1, kElementContext1,
-                     ExpectedCall::kShown, 1);
-  ExpectedCall hidden(kElementIdentifier1, kElementContext1,
-                      ExpectedCall::kHidden, 1);
-  ExpectedCall activated(kElementIdentifier1, kElementContext1,
-                         ExpectedCall::kActivated, 1);
-
-  element_tracker_->NotifyMenuWillShow(kFakeMenu1, kElementContext1);
-  element_tracker_->NotifyMenuItemShown(kFakeMenu1, kElementIdentifier1,
-                                        kScreenBounds1);
-  element_tracker_->NotifyMenuItemHidden(kFakeMenu1, kElementIdentifier1);
-  element_tracker_->NotifyMenuItemActivated(kFakeMenu1, kElementIdentifier1);
-  element_tracker_->NotifyMenuDoneShowing(kFakeMenu1);
-}
-
-TEST_F(ElementTrackerMacTest,
-       TrackerIsRobustToOutOfOrderActivationInNestedMenu) {
-  ExpectedCall shown(kElementIdentifier1, kElementContext1,
-                     ExpectedCall::kShown, 1);
-  ExpectedCall hidden(kElementIdentifier1, kElementContext1,
-                      ExpectedCall::kHidden, 1);
-  ExpectedCall activated(kElementIdentifier1, kElementContext1,
-                         ExpectedCall::kActivated, 0);
-  ExpectedCall shown2(kElementIdentifier2, kElementContext1,
-                      ExpectedCall::kShown, 1);
-  ExpectedCall hidden2(kElementIdentifier2, kElementContext1,
-                       ExpectedCall::kHidden, 1);
-  ExpectedCall activated2(kElementIdentifier2, kElementContext1,
-                          ExpectedCall::kActivated, 1);
-  element_tracker_->SetParent(kFakeMenu2, kFakeMenu1);
-  element_tracker_->NotifyMenuWillShow(kFakeMenu1, kElementContext1);
-  element_tracker_->NotifyMenuItemShown(kFakeMenu1, kElementIdentifier1,
-                                        kScreenBounds1);
-  element_tracker_->NotifyMenuItemShown(kFakeMenu2, kElementIdentifier2,
-                                        kScreenBounds2);
-  element_tracker_->NotifyMenuItemHidden(kFakeMenu2, kElementIdentifier2);
-  element_tracker_->NotifyMenuItemHidden(kFakeMenu1, kElementIdentifier1);
-  element_tracker_->NotifyMenuItemActivated(kFakeMenu2, kElementIdentifier2);
-  element_tracker_->NotifyMenuDoneShowing(kFakeMenu1);
-}
-
 }  // namespace ui
diff --git a/ui/base/mojom/BUILD.gn b/ui/base/mojom/BUILD.gn
index 29b1b34..d9678bdc 100644
--- a/ui/base/mojom/BUILD.gn
+++ b/ui/base/mojom/BUILD.gn
@@ -20,19 +20,6 @@
     ]
   }
 
-  shared_cpp_typemaps = [
-    {
-      types = [
-        {
-          mojom = "ui.mojom.WindowShowState"
-          cpp = "::ui::WindowShowState"
-        },
-      ]
-      traits_headers = [ "ui_base_types_mojom_traits.h" ]
-      traits_public_deps = [ "//ui/base" ]
-    },
-  ]
-
   cpp_typemaps = [
     {
       types = [
@@ -56,9 +43,6 @@
     },
   ]
 
-  cpp_typemaps += shared_cpp_typemaps
-  blink_cpp_typemaps = shared_cpp_typemaps
-
   export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
   export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
   export_header_blink = "third_party/blink/public/platform/web_common.h"
@@ -69,6 +53,11 @@
 }
 
 # TODO(crbug.com/359910414): Move back DialogButton enum to ui_base_types.mojom.
+# TODO(crbug.com/361008605): Move back `WindowShowState` enum to ui_base_types.mojom.
 mojom("ui_base_types") {
-  sources = [ "dialog_button.mojom" ]
+  generate_java = true
+  sources = [
+    "dialog_button.mojom",
+    "window_show_state.mojom",
+  ]
 }
diff --git a/ui/base/mojom/ui_base_types.mojom b/ui/base/mojom/ui_base_types.mojom
index 7d39599..f25fb226 100644
--- a/ui/base/mojom/ui_base_types.mojom
+++ b/ui/base/mojom/ui_base_types.mojom
@@ -36,15 +36,3 @@
   ADJUST_SELECTION,
   ADJUST_SELECTION_RESET
 };
-
-// Mapped to ui::WindowShowState. Any new type here needs to be synced with
-// ui::MenuSourceType in ui_base_types.h.
-[Stable, Extensible]
-enum WindowShowState {
-  [Default] SHOW_STATE_DEFAULT = 0,
-  SHOW_STATE_NORMAL = 1,
-  SHOW_STATE_MINIMIZED = 2,
-  SHOW_STATE_MAXIMIZED = 3,
-  SHOW_STATE_INACTIVE = 4,
-  SHOW_STATE_FULLSCREEN = 5
-};
diff --git a/ui/base/mojom/ui_base_types_mojom_traits.h b/ui/base/mojom/ui_base_types_mojom_traits.h
index b85064c..0bdfff1 100644
--- a/ui/base/mojom/ui_base_types_mojom_traits.h
+++ b/ui/base/mojom/ui_base_types_mojom_traits.h
@@ -84,55 +84,6 @@
   }
 };
 
-template <>
-struct EnumTraits<ui::mojom::WindowShowState, ui::WindowShowState> {
-  static ui::mojom::WindowShowState ToMojom(
-      ui::WindowShowState window_show_state) {
-    switch (window_show_state) {
-      case ui::SHOW_STATE_DEFAULT:
-        return ui::mojom::WindowShowState::SHOW_STATE_DEFAULT;
-      case ui::SHOW_STATE_FULLSCREEN:
-        return ui::mojom::WindowShowState::SHOW_STATE_FULLSCREEN;
-      case ui::SHOW_STATE_INACTIVE:
-        return ui::mojom::WindowShowState::SHOW_STATE_INACTIVE;
-      case ui::SHOW_STATE_MINIMIZED:
-        return ui::mojom::WindowShowState::SHOW_STATE_MINIMIZED;
-      case ui::SHOW_STATE_MAXIMIZED:
-        return ui::mojom::WindowShowState::SHOW_STATE_MAXIMIZED;
-      case ui::SHOW_STATE_NORMAL:
-        return ui::mojom::WindowShowState::SHOW_STATE_NORMAL;
-      case ui::SHOW_STATE_END:
-        NOTREACHED();
-    }
-    NOTREACHED();
-  }
-
-  static bool FromMojom(ui::mojom::WindowShowState window_show_state,
-                        ui::WindowShowState* out) {
-    switch (window_show_state) {
-      case ui::mojom::WindowShowState::SHOW_STATE_DEFAULT:
-        *out = ui::SHOW_STATE_DEFAULT;
-        return true;
-      case ui::mojom::WindowShowState::SHOW_STATE_FULLSCREEN:
-        *out = ui::SHOW_STATE_FULLSCREEN;
-        return true;
-      case ui::mojom::WindowShowState::SHOW_STATE_INACTIVE:
-        *out = ui::SHOW_STATE_INACTIVE;
-        return true;
-      case ui::mojom::WindowShowState::SHOW_STATE_MINIMIZED:
-        *out = ui::SHOW_STATE_MINIMIZED;
-        return true;
-      case ui::mojom::WindowShowState::SHOW_STATE_MAXIMIZED:
-        *out = ui::SHOW_STATE_MAXIMIZED;
-        return true;
-      case ui::mojom::WindowShowState::SHOW_STATE_NORMAL:
-        *out = ui::SHOW_STATE_NORMAL;
-        return true;
-    }
-    NOTREACHED();
-  }
-};
-
 }  // namespace mojo
 
 #endif  // UI_BASE_MOJOM_UI_BASE_TYPES_MOJOM_TRAITS_H_
diff --git a/ui/base/mojom/window_show_state.mojom b/ui/base/mojom/window_show_state.mojom
new file mode 100644
index 0000000..526b6129
--- /dev/null
+++ b/ui/base/mojom/window_show_state.mojom
@@ -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.
+
+module ui.mojom;
+
+// This enum must be version-skew tolerant. It is persisted to disk by ChromeOS
+// full restore, and read from disk by a possibly newer version of chrome. This
+// means that it's ok to add new values, but existing values should never be
+// changed or removed.
+//
+// Window "show" state.
+// TODO: Add snapped window state to immersive fullscreen state to
+// WindowShowState. Those are ChromeOS specific window states but we should make
+// it available here as well as Lacros also needs to know those states.
+// TODO(crbug.com/361008605): Move back to ui_base_types.mojom and delete
+// this file.
+[Stable, Extensible]
+enum WindowShowState {
+  [Default] kDefault = 0,
+  kNormal = 1,
+  kMinimized = 2,
+  kMaximized = 3,
+  kInactive = 4, // Views only, not persisted.
+  kFullscreen = 5,
+  // TODO(crbug.com/361560784): Investigate and Remove `kEnd`
+  [MinVersion=1] kEnd = 6,
+};
diff --git a/ui/base/test/mock_base_window.h b/ui/base/test/mock_base_window.h
index 9131c56..4d954f7c 100644
--- a/ui/base/test/mock_base_window.h
+++ b/ui/base/test/mock_base_window.h
@@ -7,6 +7,7 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/base_window.h"
+#include "ui/base/mojom/window_show_state.mojom-forward.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace ui {
@@ -27,7 +28,7 @@
   MOCK_METHOD(bool, IsFullscreen, (), (const));
   MOCK_METHOD(gfx::NativeWindow, GetNativeWindow, (), (const));
   MOCK_METHOD(gfx::Rect, GetRestoredBounds, (), (const));
-  MOCK_METHOD(ui::WindowShowState, GetRestoredState, (), (const));
+  MOCK_METHOD(ui::mojom::WindowShowState, GetRestoredState, (), (const));
   MOCK_METHOD(gfx::Rect, GetBounds, (), (const));
   MOCK_METHOD(void, Show, (), ());
   MOCK_METHOD(void, Hide, (), ());
diff --git a/ui/base/ui_base_types.h b/ui/base/ui_base_types.h
index 8dae0550..c3f5337 100644
--- a/ui/base/ui_base_types.h
+++ b/ui/base/ui_base_types.h
@@ -7,27 +7,30 @@
 
 #include <cstdint>
 
+#include "ui/base/mojom/window_show_state.mojom.h"
+
 namespace ui {
 
-// This enum must be version-skew tolerant. It is persisted to disk by ChromeOS
-// full restore, and read from disk by a possibly newer version of chrome. This
-// means that it's ok to add new values, but existing values should never be
-// changed or removed.
-//
-// Window "show" state.
-// TODO: Add snapped window state to immersive fullscreen state to
-// WindowShowState. Those are ChromeOS specific window states but we should make
-// it available here as well as Lacros also needs to know those states.
-enum WindowShowState {
-  // A default un-set state.
-  SHOW_STATE_DEFAULT = 0,
-  SHOW_STATE_NORMAL = 1,
-  SHOW_STATE_MINIMIZED = 2,
-  SHOW_STATE_MAXIMIZED = 3,
-  SHOW_STATE_INACTIVE = 4,  // Views only, not persisted.
-  SHOW_STATE_FULLSCREEN = 5,
-  SHOW_STATE_END = 6  // The end of show state enum.
-};
+// Alias until all clients of `WindowShowState` have been migrated.
+// Will be removed once that is complete.
+using WindowShowState = ::ui::mojom::WindowShowState;
+
+// Alias for the old enumerators of `WindowShowState` until all clients have
+// been migrated. Will be removed once that is complete.
+inline constexpr WindowShowState SHOW_STATE_DEFAULT =
+    ui::mojom::WindowShowState::kDefault;
+inline constexpr WindowShowState SHOW_STATE_NORMAL =
+    ui::mojom::WindowShowState::kNormal;
+inline constexpr WindowShowState SHOW_STATE_MINIMIZED =
+    ui::mojom::WindowShowState::kMinimized;
+inline constexpr WindowShowState SHOW_STATE_MAXIMIZED =
+    ui::mojom::WindowShowState::kMaximized;
+inline constexpr WindowShowState SHOW_STATE_INACTIVE =
+    ui::mojom::WindowShowState::kInactive;
+inline constexpr WindowShowState SHOW_STATE_FULLSCREEN =
+    ui::mojom::WindowShowState::kFullscreen;
+inline constexpr WindowShowState SHOW_STATE_END =
+    ui::mojom::WindowShowState::kEnd;
 
 // Specifies which edges of the window are tiled.
 //
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index 71a9ce1..6884198 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -481,6 +481,7 @@
   E_CPONLY(kColorThrobber) \
   E_CPONLY(kColorThrobberPreconnect) \
   E_CPONLY(kColorToastBackground) \
+  E_CPONLY(kColorToastBackgroundProminent) \
   E_CPONLY(kColorToastButton) \
   E_CPONLY(kColorToastForeground) \
   E_CPONLY(kColorToggleButtonHover) \
diff --git a/ui/color/material_ui_color_mixer.cc b/ui/color/material_ui_color_mixer.cc
index f7e364d..27381ce4 100644
--- a/ui/color/material_ui_color_mixer.cc
+++ b/ui/color/material_ui_color_mixer.cc
@@ -176,7 +176,8 @@
   mixer[kColorThemeColorPickerHueSliderHandle] = {kColorSysWhite};
   mixer[kColorThemeColorPickerOptionBackground] = {kColorSysNeutralContainer};
   mixer[kColorThrobber] = {kColorSysPrimary};
-  mixer[kColorToastBackground] = {kColorSysInverseSurfacePrimary};
+  mixer[kColorToastBackground] = {kColorSysInverseSurface};
+  mixer[kColorToastBackgroundProminent] = {kColorSysInverseSurfacePrimary};
   mixer[kColorToastButton] = {kColorSysInversePrimary};
   mixer[kColorToastForeground] = {kColorSysInverseOnSurface};
   mixer[kColorToggleButtonHover] = {kColorSysStateHover};
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index dbbc098..926631f 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -79,22 +79,6 @@
 // character to belong to more scripts.
 const size_t kMaxScripts = 32;
 
-// Font fallback mechanism used to Shape runs (see ShapeRuns(...)).
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class ShapeRunFallback {
-  FAILED = 0,
-  NO_FALLBACK = 1,
-  FALLBACK = 2,
-  FALLBACKS = 3,
-  kMaxValue = FALLBACKS
-};
-
-// Log the fallback font mechanism used for shaping to UMA (see ShapeRuns(...)).
-void RecordShapeRunsFallback(ShapeRunFallback fallback) {
-  UMA_HISTOGRAM_ENUMERATION("RenderTextHarfBuzz.ShapeRunsFallback", fallback);
-}
-
 // Returns whether the codepoint has the 'extended pictographic' property.
 bool IsExtendedPictographicCodepoint(UChar32 codepoint) {
   return u_hasBinaryProperty(codepoint, UCHAR_EXTENDED_PICTOGRAPHIC);
@@ -1977,6 +1961,9 @@
   // prior step.
   if (!successfully_shaped_runs && !ignore_missing_glyph_breaks_for_test_) {
     BuildResolvedTypefaceBreakList(run_list);
+
+    // TODO(kschmi): Only re-shape if `BuildResolvedTypefaceBreakList` made a
+    // difference.
     ItemizeAndShapeTextImpl(&commonized_run_map, text, run_list);
 
     // Resolved typefaces are no longer used and can be cleared.
@@ -1984,6 +1971,10 @@
     resolved_typefaces().Reset();
   }
 
+  // Now that potentially two passes to ItemizeAndShapeTextImpl have occurred,
+  // we can record the final type of fallback.
+  EmitShapeRunsFallback();
+
   run_list->InitIndexMap();
   run_list->ComputePrecedingRunWidths();
 
@@ -2141,7 +2132,7 @@
   }
   runs.swap(need_shaping_runs);
   if (runs.empty()) {
-    RecordShapeRunsFallback(ShapeRunFallback::NO_FALLBACK);
+    RecordShapeRunsFallback(internal::ShapeRunFallback::NO_FALLBACK);
     return true;
   }
 
@@ -2162,7 +2153,7 @@
       fallback_font_candidates.push_back(font);
     }
     if (runs.empty()) {
-      RecordShapeRunsFallback(ShapeRunFallback::NO_FALLBACK);
+      RecordShapeRunsFallback(internal::ShapeRunFallback::NO_FALLBACK);
       return true;
     }
   }
@@ -2211,7 +2202,7 @@
   }
   runs.swap(remaining_unshaped_runs);
   if (runs.empty()) {
-    RecordShapeRunsFallback(ShapeRunFallback::FALLBACK);
+    RecordShapeRunsFallback(internal::ShapeRunFallback::FALLBACK);
     return true;
   }
 
@@ -2291,7 +2282,7 @@
                              TRACE_EVENT_SCOPE_THREAD, "font_name",
                              TRACE_STR_COPY(font_name.c_str()),
                              "primary_font_name", primary_font.GetFontName());
-        RecordShapeRunsFallback(ShapeRunFallback::FALLBACKS);
+        RecordShapeRunsFallback(internal::ShapeRunFallback::FALLBACKS);
         // Resolving fallback fonts using the registry keys on windows will be
         // deprecated and removed (see: http://crbug.com/995789). The crashes
         // reported here should be fixed before deprecating the code.
@@ -2321,7 +2312,7 @@
     }
   }
 
-  RecordShapeRunsFallback(ShapeRunFallback::FAILED);
+  RecordShapeRunsFallback(internal::ShapeRunFallback::FAILED);
   return false;
 }
 
@@ -2436,6 +2427,18 @@
   }
 }
 
+void RenderTextHarfBuzz::RecordShapeRunsFallback(
+    internal::ShapeRunFallback fallback) {
+  last_shape_run_metric_.emplace(fallback);
+}
+
+void RenderTextHarfBuzz::EmitShapeRunsFallback() {
+  if (last_shape_run_metric_.has_value()) {
+    UMA_HISTOGRAM_ENUMERATION("RenderTextHarfBuzz.ShapeRunsFallback",
+                              last_shape_run_metric_.value());
+  }
+}
+
 void RenderTextHarfBuzz::GetDecoratedTextForRange(
     const Range& text_range,
     DecoratedText* decorated_text) {
diff --git a/ui/gfx/render_text_harfbuzz.h b/ui/gfx/render_text_harfbuzz.h
index 6ea1761b..12dc9a8 100644
--- a/ui/gfx/render_text_harfbuzz.h
+++ b/ui/gfx/render_text_harfbuzz.h
@@ -28,6 +28,17 @@
 
 namespace internal {
 
+// Font fallback mechanism used to Shape runs (see ShapeRuns(...)).
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ShapeRunFallback {
+  FAILED = 0,
+  NO_FALLBACK = 1,
+  FALLBACK = 2,
+  FALLBACKS = 3,
+  kMaxValue = FALLBACKS
+};
+
 struct GFX_EXPORT TextRunHarfBuzz {
   // Construct the run with |template_font| since determining the details of a
   // default-constructed gfx::Font is expensive, but it will always be replaced.
@@ -328,6 +339,14 @@
   // pass.
   bool IsValidDisplayRange(Range display_range);
 
+  // Store the fallback font mechanism used for shaping (see ShapeRuns(...)).
+  void RecordShapeRunsFallback(internal::ShapeRunFallback fallback);
+
+  // Record the fallback font mechanism used for shaping to UMA (see
+  // ShapeRuns(...)). This is done separately from RecordShapeRunsFallback, as
+  // multiple passes may be involved and we only want to log the final pass.
+  void EmitShapeRunsFallback();
+
   // RenderText:
   internal::TextRunList* GetRunList() override;
   const internal::TextRunList* GetRunList() const override;
@@ -351,6 +370,9 @@
 
   // The process application locale used to configure text rendering.
   std::string locale_;
+
+  // The fallback result of the most recent call to ShapeRuns.
+  std::optional<internal::ShapeRunFallback> last_shape_run_metric_;
 };
 
 }  // namespace gfx
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 14d80d1..ecd3517 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -7057,8 +7057,14 @@
   EXPECT_EQ(std::vector<std::u16string>({u"ꟺM"}), GetRunListStrings());
   EXPECT_EQ("[0->1]", GetRunListStructureString());
 #else
+  base::HistogramTester histograms;
   EXPECT_EQ(std::vector<std::u16string>({u"ꟺ", u"M"}), GetRunListStrings());
   EXPECT_EQ("[0][1]", GetRunListStructureString());
+
+  // There should be one bucket with two entries (one per run).
+  histograms.ExpectTotalCount("RenderTextHarfBuzz.ShapeRunsFallback", 1);
+  EXPECT_EQ(histograms.GetTotalSum("RenderTextHarfBuzz.ShapeRunsFallback"), 2);
+
 #endif  // BUILDFLAG(IS_ANDROID)
   CheckBoundsForCursorPositions();
 }
diff --git a/ui/gl/swap_chain_presenter.cc b/ui/gl/swap_chain_presenter.cc
index 1ee321a..af9b3364 100644
--- a/ui/gl/swap_chain_presenter.cc
+++ b/ui/gl/swap_chain_presenter.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "ui/gl/swap_chain_presenter.h"
 
 #include <d3d11_1.h>
@@ -637,21 +632,34 @@
 
   size_t dest_stride = mapped_resource.RowPitch;
   DCHECK_GE(dest_stride, static_cast<size_t>(texture_size.width()));
+  // y-plane size.
+  size_t src_size = pixmap_stride * texture_size.height();
+  size_t dest_size = dest_stride * texture_size.height();
+  if (texture_size.height() / 2 > 0) {
+    // uv-plane size. Note that the last row is actual texture width, not
+    // the stride.
+    src_size +=
+        pixmap_stride * (texture_size.height() / 2 - 1) + texture_size.width();
+    dest_size +=
+        dest_stride * (texture_size.height() / 2 - 1) + texture_size.width();
+  }
+  base::span<const uint8_t> src =
+      UNSAFE_TODO(base::span(nv12_pixmap, src_size));
+  // SAFETY: required from Map() call result.
+  base::span<uint8_t> dest = UNSAFE_BUFFERS(
+      base::span(reinterpret_cast<uint8_t*>(mapped_resource.pData), dest_size));
   for (int y = 0; y < texture_size.height(); y++) {
-    const uint8_t* src_row = nv12_pixmap + pixmap_stride * y;
-    uint8_t* dest_row =
-        reinterpret_cast<uint8_t*>(mapped_resource.pData) + dest_stride * y;
-    memcpy(dest_row, src_row, texture_size.width());
+    auto src_row = src.subspan(pixmap_stride * y, texture_size.width());
+    auto dest_row = dest.subspan(dest_stride * y, texture_size.width());
+    dest_row.copy_prefix_from(src_row);
   }
 
-  const uint8_t* uv_src_start =
-      nv12_pixmap + pixmap_stride * texture_size.height();
-  uint8_t* uv_dst_start = reinterpret_cast<uint8_t*>(mapped_resource.pData) +
-                          dest_stride * texture_size.height();
+  auto uv_src = src.subspan(pixmap_stride * texture_size.height());
+  auto uv_dest = dest.subspan(dest_stride * texture_size.height());
   for (int y = 0; y < texture_size.height() / 2; y++) {
-    const uint8_t* src_row = uv_src_start + pixmap_stride * y;
-    uint8_t* dest_row = uv_dst_start + dest_stride * y;
-    memcpy(dest_row, src_row, texture_size.width());
+    auto src_row = uv_src.subspan(pixmap_stride * y, texture_size.width());
+    auto dest_row = uv_dest.subspan(dest_stride * y, texture_size.width());
+    dest_row.copy_prefix_from(src_row);
   }
   context->Unmap(staging_texture_.Get(), 0);
 
@@ -1610,9 +1618,9 @@
     staging_texture_.Reset();
     copy_texture_.Reset();
   } else {
-    input_texture = UploadVideoImage(params.overlay_image->size(),
-                                     params.overlay_image->nv12_pixmap(),
-                                     params.overlay_image->pixmap_stride());
+    input_texture = UNSAFE_TODO(UploadVideoImage(
+        params.overlay_image->size(), params.overlay_image->nv12_pixmap(),
+        params.overlay_image->pixmap_stride()));
     input_level = 0;
   }
 
diff --git a/ui/gl/swap_chain_presenter.h b/ui/gl/swap_chain_presenter.h
index 849cebf..a2e884c 100644
--- a/ui/gl/swap_chain_presenter.h
+++ b/ui/gl/swap_chain_presenter.h
@@ -11,6 +11,7 @@
 #include <dcomp.h>
 #include <wrl/client.h>
 
+#include "base/compiler_specific.h"
 #include "base/containers/circular_deque.h"
 #include "base/memory/raw_ptr.h"
 #include "base/power_monitor/power_monitor.h"
@@ -91,7 +92,7 @@
 
   // Upload given YUV buffers to an NV12 texture that can be used to create
   // video processor input view.  Returns nullptr on failure.
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> UploadVideoImage(
+  UNSAFE_BUFFER_USAGE Microsoft::WRL::ComPtr<ID3D11Texture2D> UploadVideoImage(
       const gfx::Size& size,
       const uint8_t* nv12_pixmap,
       size_t stride);
diff --git a/ui/message_center/public/cpp/notification.cc b/ui/message_center/public/cpp/notification.cc
index 3bb00bad..ff694645 100644
--- a/ui/message_center/public/cpp/notification.cc
+++ b/ui/message_center/public/cpp/notification.cc
@@ -79,29 +79,38 @@
   icon_.emplace(std::move(icon));
 }
 
-NotificationItem::NotificationItem(const NotificationItem& other) = default;
-
 NotificationItem::NotificationItem() = default;
 
-NotificationItem::~NotificationItem() = default;
+NotificationItem::NotificationItem(const NotificationItem& other) = default;
+
+NotificationItem::NotificationItem(NotificationItem&& other) = default;
 
 NotificationItem& NotificationItem::operator=(const NotificationItem& other) =
     default;
 
+NotificationItem& NotificationItem::operator=(NotificationItem&& other) =
+    default;
+
+NotificationItem::~NotificationItem() = default;
+
 ButtonInfo::ButtonInfo(const std::u16string& title) : title(title) {}
 
 ButtonInfo::ButtonInfo(const gfx::VectorIcon* vector_icon,
                        const std::u16string& accessible_name)
     : vector_icon(vector_icon), accessible_name(accessible_name) {}
 
-ButtonInfo::ButtonInfo(const ButtonInfo& other) = default;
-
 ButtonInfo::ButtonInfo() = default;
 
-ButtonInfo::~ButtonInfo() = default;
+ButtonInfo::ButtonInfo(const ButtonInfo& other) = default;
+
+ButtonInfo::ButtonInfo(ButtonInfo&& other) = default;
 
 ButtonInfo& ButtonInfo::operator=(const ButtonInfo& other) = default;
 
+ButtonInfo& ButtonInfo::operator=(ButtonInfo&& other) = default;
+
+ButtonInfo::~ButtonInfo() = default;
+
 RichNotificationData::RichNotificationData() : timestamp(base::Time::Now()) {}
 
 RichNotificationData::RichNotificationData(const RichNotificationData& other) =
@@ -144,8 +153,12 @@
 
 Notification::Notification(const Notification& other) = default;
 
+Notification::Notification(Notification&& other) = default;
+
 Notification& Notification::operator=(const Notification& other) = default;
 
+Notification& Notification::operator=(Notification&& other) = default;
+
 Notification::~Notification() = default;
 
 // static
diff --git a/ui/message_center/public/cpp/notification.h b/ui/message_center/public/cpp/notification.h
index b1646cc..41108f5 100644
--- a/ui/message_center/public/cpp/notification.h
+++ b/ui/message_center/public/cpp/notification.h
@@ -45,10 +45,13 @@
   NotificationItem(const std::u16string& title,
                    const std::u16string& message,
                    ui::ImageModel icon = ui::ImageModel());
-  NotificationItem(const NotificationItem& other);
+
   NotificationItem();
-  ~NotificationItem();
+  NotificationItem(const NotificationItem& other);
+  NotificationItem(NotificationItem&& other);
   NotificationItem& operator=(const NotificationItem& other);
+  NotificationItem& operator=(NotificationItem&& other);
+  ~NotificationItem();
 
   const std::u16string& title() const { return title_; }
   const std::u16string& message() const { return message_; }
@@ -89,10 +92,12 @@
   explicit ButtonInfo(const std::u16string& title);
   ButtonInfo(const gfx::VectorIcon* vector_icon,
              const std::u16string& accessible_name);
-  ButtonInfo(const ButtonInfo& other);
   ButtonInfo();
-  ~ButtonInfo();
+  ButtonInfo(const ButtonInfo& other);
+  ButtonInfo(ButtonInfo&& other);
   ButtonInfo& operator=(const ButtonInfo& other);
+  ButtonInfo& operator=(ButtonInfo&& other);
+  ~ButtonInfo();
 
   // Title that should be displayed on the notification button.
   std::u16string title;
@@ -322,7 +327,9 @@
   // identical for both the Notification instances.
   Notification(const Notification& other);
 
+  Notification(Notification&& other);
   Notification& operator=(const Notification& other);
+  Notification& operator=(Notification&& other);
 
   virtual ~Notification();
 
diff --git a/ui/shell_dialogs/BUILD.gn b/ui/shell_dialogs/BUILD.gn
index c8501fd..7d2bfca0 100644
--- a/ui/shell_dialogs/BUILD.gn
+++ b/ui/shell_dialogs/BUILD.gn
@@ -54,10 +54,7 @@
       "shell_dialog_linux.cc",
       "shell_dialog_linux.h",
     ]
-    deps += [
-      "//components/dbus/thread_linux",
-      "//ui/linux:linux_ui",
-    ]
+    deps += [ "//ui/linux:linux_ui" ]
     if (use_dbus) {
       defines += [ "USE_DBUS" ]
       sources += [
@@ -65,6 +62,7 @@
         "select_file_dialog_linux_portal.h",
       ]
       deps += [
+        "//components/dbus/thread_linux",
         "//dbus",
         "//ui/views",
       ]
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index d5be69c8..a3f2f19 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -1094,8 +1094,10 @@
 void NativeWidgetAura::OnDeviceScaleFactorChanged(
     float old_device_scale_factor,
     float new_device_scale_factor) {
-  GetWidget()->DeviceScaleFactorChanged(old_device_scale_factor,
-                                        new_device_scale_factor);
+  if (Widget* widget = GetWidget()) {
+    widget->DeviceScaleFactorChanged(old_device_scale_factor,
+                                     new_device_scale_factor);
+  }
 }
 
 void NativeWidgetAura::OnWindowDestroying(aura::Window* window) {
diff --git a/ui/views/widget/native_widget_aura_unittest.cc b/ui/views/widget/native_widget_aura_unittest.cc
index eaf9443fc..43de28a6 100644
--- a/ui/views/widget/native_widget_aura_unittest.cc
+++ b/ui/views/widget/native_widget_aura_unittest.cc
@@ -324,7 +324,7 @@
  private:
   gfx::NativeWindow window_;
   int count_ = 0;
-  ui::WindowShowState state_ = ui::WindowShowState::SHOW_STATE_DEFAULT;
+  ui::WindowShowState state_ = ui::SHOW_STATE_DEFAULT;
 };
 
 // Tests that window transitions from normal to minimized and back do not
@@ -343,18 +343,18 @@
   widget->Show();
   EXPECT_FALSE(widget->IsMinimized());
   EXPECT_EQ(0, observer->count());
-  EXPECT_EQ(ui::WindowShowState::SHOW_STATE_DEFAULT, observer->state());
+  EXPECT_EQ(ui::SHOW_STATE_DEFAULT, observer->state());
 
   widget->Minimize();
   EXPECT_TRUE(widget->IsMinimized());
   EXPECT_EQ(1, observer->count());
-  EXPECT_EQ(ui::WindowShowState::SHOW_STATE_MINIMIZED, observer->state());
+  EXPECT_EQ(ui::SHOW_STATE_MINIMIZED, observer->state());
   observer->Reset();
 
   widget->Show();
   widget->Restore();
   EXPECT_EQ(1, observer->count());
-  EXPECT_EQ(ui::WindowShowState::SHOW_STATE_NORMAL, observer->state());
+  EXPECT_EQ(ui::SHOW_STATE_NORMAL, observer->state());
 
   observer.reset();
   EXPECT_FALSE(widget->IsMinimized());
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc
index 05cac1b..9b9323b 100644
--- a/ui/views/window/dialog_delegate_unittest.cc
+++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -424,7 +424,7 @@
   // Set the initial focus while the Widget is unactivated to prevent the
   // initially focused View from receiving focus. Use a minimised state here to
   // prevent the Widget from being activated while this happens.
-  dialog_widget->SetInitialFocus(ui::WindowShowState::SHOW_STATE_MINIMIZED);
+  dialog_widget->SetInitialFocus(ui::SHOW_STATE_MINIMIZED);
 
   // Nothing should be focused, because the Widget is still deactivated.
   EXPECT_EQ(nullptr, dialog_widget->GetFocusManager()->GetFocusedView());
diff --git a/v8 b/v8
index b5ecf9a..8fec6db 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit b5ecf9ac14f0683748aaa8f2bce6b239f434d9ae
+Subproject commit 8fec6dbcd949d2b6b1d84ca3758cac44582968bd