diff --git a/BUILD.gn b/BUILD.gn
index 357f711..8f91d4f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1135,6 +1135,28 @@
         "//third_party/blink/tools:wpt_tests_isolate",
       ]
     }
+    script_test("headless_shell_wpt") {
+      script = "//third_party/blink/tools/run_wpt_tests.py"
+      args = [
+        "--test-type",
+        "testharness",
+        "reftest",
+        "crashtest",
+        "print-reftest",
+        "--product=headless_shell",
+        "--no-virtual-tests",
+        "--no-wpt-internal",
+        "--no-show-results",
+        "--zero-tests-executed-ok",
+      ]
+      data_deps = [
+        ":blink_web_tests_expectations",
+        ":blink_web_tests_support_data",
+        "//chrome:chrome",
+        "//chrome/test/chromedriver:chromedriver_server",
+        "//third_party/blink/tools:wpt_tests_isolate",
+      ]
+    }
   }
 
   group("blink_web_tests_support_data") {
diff --git a/DEPS b/DEPS
index 321232f4..ec383d1 100644
--- a/DEPS
+++ b/DEPS
@@ -308,19 +308,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': 'df97b0f7451528e858398030e718d637a29aaa5f',
+  'src_internal_revision': '51784961a549aa3eff970c32af08734422989955',
   # 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': '2790777048d3f00384f68be8081f3a3db615746c',
+  'skia_revision': 'a3a016537a8c512df42b50522e78710b320c0faf',
   # 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': '628dd1909b2405aa0a68f9bd1aa00bbe5bc717b0',
+  'v8_revision': '3d675d1ac3f96a60508bffa312e48bd46ddb466f',
   # 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': '313c73c3f6a056a049c03c4f928ab5d83f17a77a',
+  'angle_revision': 'd71b8ee0f0e26b14a8fa642460df2635c2d7db2f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -363,7 +363,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'd091bca546fa15928db36c8447e126ee43ddb5f4',
+  'freetype_revision': '12adfc212bd2f7560e1e175e66458124f9bd554b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -383,7 +383,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '7d44c8067842719b399fb10f625e573e29cb723b',
+  'catapult_revision': 'e939ac77bb9471acc10f49e82cfe65790068c3d1',
   # 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.
@@ -827,7 +827,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'ef9ed7638a0a413170457f22032944d1b61febbb',
+    '4ccf9ec2cc92967d39ca0e3d0b0158bfd7937c81',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -836,7 +836,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'a09fa0c5cf18578d1985f3abffb599ecdc6fd514',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '26acfe36928095395dfcb6ff3d0578550bd868ac',
       'condition': 'checkout_ios',
   },
 
@@ -982,7 +982,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'kp4Klz8ufJ2EgDaTvWPyNBOHWCPWMYaJDGa1FzMAKP8C',
+          'version': '1qnqDwkuAyFH32YJq-GEdgF84BjauJ9_6hJa_Md5yGcC',
       },
     ],
     'condition': 'checkout_android',
@@ -1157,7 +1157,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'edee6b1f898f39a0e19e99d28be674a1aaa546a3',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8af906338f6c07813067abf296170c8a4f46c538',
       'condition': 'checkout_chromeos',
   },
 
@@ -1192,13 +1192,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ed3d513241532dbea8e324ec9e1fe767bd75bddf',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'af97284b58afb6bdb7dd2b353bc651c718ce5bb4',
 
   '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' + '@' + '904930d0bf12b7030892a118a2d2a6817dde61a3',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '84f68106afc210db30fb1efa8f76425cb067e82b',
     'condition': 'checkout_src_internal',
   },
 
@@ -1652,7 +1652,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '09a4f3ec842a8932341b195c5b01e141c8a16eb7',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '624f91b189a5a6fd35b1d3c43c60678c1f111dff',
+    Var('chromium_git') + '/openscreen' + '@' + 'dcd61dfe0e1e6c27d6d48fd4a29a9117e7d4b666',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '95fe35ffb383710a6e0567e958ead9a3b66e930c',
@@ -1663,7 +1663,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f6656e9828f5a5a5358d0782bd4d5cdecdc652f5',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '234fd02711c642ec5211da272283db8fd8d91af4',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '8ef97ff3b7332e38e61b347a2fbed425a4617151',
@@ -1811,7 +1811,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@01c82a000dcec3cc0452ef2faee80167fd788b79',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@6066c0d57a8bd7d68060336c7b01eeb9a1271589',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -1848,7 +1848,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'bc3c8bad295ae0ba7f0ddb18848df70f92a820c0',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '11eade521a44d63af95cc72cfbbfa6c6becf972f',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '0da8f2f1189d05814b5bbfd770f362928f2fb829',
 
   'src/third_party/webrtc':
     Var('webrtc_git') + '/src.git' + '@' + '85bea5a11bf3e771b335671b9875b8a9b033f223',
@@ -1985,7 +1985,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'M1WdGLxXi9-fM7KnNtTP2IezFumjhaN7cZy9zMF6gKUC',
+        'version': 'tX5EJQ1TlaqN-BaDUAHJ-JgOCALXI5p2m9a91Aj5KA4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4125,7 +4125,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '8af50d3b84698254f56ba6ffd69171a2abce3e12',
+        '4e9a9e1ff027107175ef685b140354acc5bfc1a9',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 096f372..62a31cc 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1980,6 +1980,9 @@
                   '|components/cbor/'\
                   '|AndroidManifest',
     },
+    'security_interstitials': {
+      'filepath': 'components/security_interstitials',
+    },
     'select_to_speak': {
       'filepath': 'select_to_speak',
     },
@@ -3205,6 +3208,7 @@
     'search_engine_choice_screen': ['chrome-waffle-eng+watch@google.com'],
     'search_prefetch': ['lingqi+watch@chromium.org'],
     'security': ['security-watchlist@chromium.org'],
+    'security_interstitials': ['drubery+watch@chromium.org'],
     'select_to_speak': ['katie+watch@chromium.org',
                         'anastasi+watch@google.com'],
     'send_tab_to_self': ['jeffreycohen+watch-send_tab_to_self@chromium.org',
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn
index 07c860f..14d6f27 100644
--- a/android_webview/browser/BUILD.gn
+++ b/android_webview/browser/BUILD.gn
@@ -116,8 +116,6 @@
     "aw_web_ui_controller_factory.h",
     "component_updater/first_party_sets_component_loader.cc",
     "component_updater/first_party_sets_component_loader.h",
-    "component_updater/loader_policies/empty_component_loader_policy.cc",
-    "component_updater/loader_policies/empty_component_loader_policy.h",
     "component_updater/loader_policies/origin_trials_component_loader_policy.cc",
     "component_updater/loader_policies/origin_trials_component_loader_policy.h",
     "component_updater/masked_domain_list_component_loader.cc",
diff --git a/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.cc b/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.cc
deleted file mode 100644
index 721dbac..0000000
--- a/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.cc
+++ /dev/null
@@ -1,65 +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 "android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "android_webview/common/aw_features.h"
-#include "base/containers/flat_map.h"
-#include "base/feature_list.h"
-#include "base/files/scoped_file.h"
-#include "base/values.h"
-#include "base/version.h"
-#include "components/component_updater/android/component_loader_policy.h"
-
-namespace android_webview {
-
-namespace {
-
-// Persisted to logs, should never change.
-constexpr char kEmptyComponentLoaderPolicyMetricsSuffix[] =
-    "WebViewEmptyComponent";
-
-// A fake SHA256 PublicKey of jebgalgnebhfojomionfpkfelancnnkf
-const uint8_t kFakePublicKeySHA256[32] = {
-    0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec, 0x8e, 0xd5, 0xfa,
-    0x54, 0xb0, 0xd2, 0xdd, 0xa5, 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47,
-    0xf6, 0xc4, 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};
-
-}  // namespace
-
-void EmptyComponentLoaderPolicy::ComponentLoaded(
-    const base::Version& /*version*/,
-    base::flat_map<std::string, base::ScopedFD>& /*fd_map*/,
-    base::Value::Dict /*manifest*/) {}
-
-void EmptyComponentLoaderPolicy::ComponentLoadFailed(
-    component_updater::ComponentLoadResult /*error*/) {}
-
-void EmptyComponentLoaderPolicy::GetHash(std::vector<uint8_t>* hash) const {
-  hash->assign(kFakePublicKeySHA256,
-               kFakePublicKeySHA256 + std::size(kFakePublicKeySHA256));
-}
-
-std::string EmptyComponentLoaderPolicy::GetMetricsSuffix() const {
-  return kEmptyComponentLoaderPolicyMetricsSuffix;
-}
-
-void LoadEmptyComponent(
-    component_updater::ComponentLoaderPolicyVector& policies) {
-  if (!base::FeatureList::IsEnabled(
-          android_webview::features::kWebViewEmptyComponentLoaderPolicy)) {
-    return;
-  }
-
-  policies.push_back(std::make_unique<EmptyComponentLoaderPolicy>());
-}
-
-}  // namespace android_webview
diff --git a/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h b/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h
deleted file mode 100644
index 5a38144..0000000
--- a/android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h
+++ /dev/null
@@ -1,52 +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 ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_EMPTY_COMPONENT_LOADER_POLICY_H_
-#define ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_EMPTY_COMPONENT_LOADER_POLICY_H_
-
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/containers/flat_map.h"
-#include "base/files/scoped_file.h"
-#include "base/values.h"
-#include "components/component_updater/android/component_loader_policy.h"
-
-namespace base {
-class Version;
-}  // namespace base
-
-namespace android_webview {
-
-// A fake empty component to run experiment to measure component updater
-// performance impact.
-// TODO(crbug.com/1288006): remove this when the experiment is over.
-class EmptyComponentLoaderPolicy
-    : public component_updater::ComponentLoaderPolicy {
- public:
-  EmptyComponentLoaderPolicy() = default;
-  ~EmptyComponentLoaderPolicy() override = default;
-
-  EmptyComponentLoaderPolicy(const EmptyComponentLoaderPolicy&) = delete;
-  EmptyComponentLoaderPolicy& operator=(const EmptyComponentLoaderPolicy&) =
-      delete;
-
-  // The following methods override ComponentLoaderPolicy.
-  void ComponentLoaded(const base::Version& version,
-                       base::flat_map<std::string, base::ScopedFD>& fd_map,
-                       base::Value::Dict manifest) override;
-  void ComponentLoadFailed(
-      component_updater::ComponentLoadResult error) override;
-  void GetHash(std::vector<uint8_t>* hash) const override;
-  std::string GetMetricsSuffix() const override;
-};
-
-void LoadEmptyComponent(
-    component_updater::ComponentLoaderPolicyVector& policies);
-
-}  // namespace android_webview
-
-#endif  // ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_EMPTY_COMPONENT_LOADER_POLICY_H_
diff --git a/android_webview/browser/component_updater/registration.cc b/android_webview/browser/component_updater/registration.cc
index b85cc7c..b6c08cb 100644
--- a/android_webview/browser/component_updater/registration.cc
+++ b/android_webview/browser/component_updater/registration.cc
@@ -5,7 +5,6 @@
 #include "android_webview/browser/component_updater/registration.h"
 
 #include "android_webview/browser/component_updater/first_party_sets_component_loader.h"
-#include "android_webview/browser/component_updater/loader_policies/empty_component_loader_policy.h"
 #include "android_webview/browser/component_updater/masked_domain_list_component_loader.h"
 #include "android_webview/browser/component_updater/origin_trials_component_loader.h"
 #include "android_webview/browser/component_updater/tpcd_metadata_component_loader.h"
@@ -20,7 +19,6 @@
   LoadTrustTokenKeyCommitmentsComponent(policies);
   LoadMaskedDomainListComponent(policies);
   LoadOriginTrialsComponent(policies);
-  LoadEmptyComponent(policies);
   LoadTpcMetadataComponent(policies);
   return policies;
 }
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc
index cd5010b..8ac02a8 100644
--- a/android_webview/common/aw_features.cc
+++ b/android_webview/common/aw_features.cc
@@ -73,12 +73,6 @@
              "WebViewDisplayCutout",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Fake empty component to measure component updater performance impact on
-// WebView clients.
-BASE_FEATURE(kWebViewEmptyComponentLoaderPolicy,
-             "WebViewEmptyComponentLoaderPolicy",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Enable the WebView Media Integrity API.
 // This feature requires `kWebViewInjectPlatformJsApis` to be enabled as well.
 BASE_FEATURE(kWebViewMediaIntegrityApi,
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h
index ba9b2202..be4e06a 100644
--- a/android_webview/common/aw_features.h
+++ b/android_webview/common/aw_features.h
@@ -21,7 +21,6 @@
 BASE_DECLARE_FEATURE(kWebViewCheckPakFileDescriptors);
 BASE_DECLARE_FEATURE(kWebViewClearFunctorInBackground);
 BASE_DECLARE_FEATURE(kWebViewDisplayCutout);
-BASE_DECLARE_FEATURE(kWebViewEmptyComponentLoaderPolicy);
 BASE_DECLARE_FEATURE(kWebViewEnumerateDevicesCache);
 BASE_DECLARE_FEATURE(kWebViewExitReasonMetric);
 BASE_DECLARE_FEATURE(kWebViewExtraHeadersSameOriginOnly);
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 970a0bf..d814ddda 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
@@ -356,9 +356,6 @@
         Flag.baseFeature(
                 NetworkServiceFeatures.MASKED_DOMAIN_LIST,
                 "When enabled, the masked domain list required for IP Protection is loaded."),
-        Flag.baseFeature(
-                AwFeatures.WEBVIEW_EMPTY_COMPONENT_LOADER_POLICY,
-                "Enables loading a fake empty (no-op) component during WebView startup."),
         Flag.commandLine(
                 AwSwitches.WEBVIEW_SELECTIVE_IMAGE_INVERSION_DARKENING,
                 "Enables use selective image inversion to automatically darken page, it will be"
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
index f63641f..517a5ea 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
@@ -9,10 +9,11 @@
 import android.webkit.JavascriptInterface;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.LargeTest;
 
 import com.google.common.util.concurrent.SettableFuture;
 
+import org.json.JSONObject;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -27,6 +28,7 @@
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content_public.browser.test.util.HistoryUtils;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.EmbeddedTestServer;
 
 import java.util.concurrent.TimeUnit;
@@ -100,10 +102,12 @@
         mTestServer.stopAndDestroyServer();
     }
 
-    private void navigateForwardAndBack() throws Throwable {
+    private void navigateForward() throws Throwable {
         mActivityTestRule.loadUrlSync(
                 mAwContents, mContentsClient.getOnPageFinishedHelper(), mForwardUrl);
+    }
 
+    private void navigateBack() throws Throwable {
         // Create a new future to avoid the future set in the initial load.
         SettableFuture<Boolean> pageFullyLoadedFuture = SettableFuture.create();
         mLoadedNotifier.setFuture(pageFullyLoadedFuture);
@@ -124,6 +128,11 @@
                 true, pageFullyLoadedFuture.get(SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    private void navigateForwardAndBack() throws Throwable {
+        navigateForward();
+        navigateBack();
+    }
+
     private boolean isPageShowPersisted() throws Exception {
         String isPersisted =
                 mActivityTestRule.executeJavaScriptAndWaitForResult(
@@ -142,8 +151,16 @@
                 "JSON.stringify(performance.getEntriesByType('navigation')[0].notRestoredReasons);");
     }
 
+    private String extractSimpleReasonString(String notRestoredReasons) throws Exception {
+        // Remove the escape character and the beginning and trailing quotes
+        notRestoredReasons = notRestoredReasons.replace("\\", "");
+        notRestoredReasons = notRestoredReasons.substring(1, notRestoredReasons.length() - 1);
+        JSONObject json_obj = new JSONObject(notRestoredReasons);
+        return json_obj.getJSONArray("reasons").getJSONObject(0).getString("reason");
+    }
+
     @Test
-    @SmallTest
+    @LargeTest
     @Feature({"AndroidWebView"})
     @CommandLineFlags.Add({
         "enable-features=WebViewBackForwardCache"
@@ -157,7 +174,7 @@
     }
 
     @Test
-    @SmallTest
+    @LargeTest
     @Feature({"AndroidWebView"})
     @CommandLineFlags.Add({
         "disable-features=WebViewBackForwardCache"
@@ -166,10 +183,65 @@
         mActivityTestRule.loadUrlSync(
                 mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
         navigateForwardAndBack();
-        String not_restored_reasons = getNotRestoredReasons();
-        Assert.assertTrue(not_restored_reasons.indexOf("reasons") >= 0);
+        String notRestoredReasons = getNotRestoredReasons();
+        Assert.assertEquals(extractSimpleReasonString(notRestoredReasons), "masked");
         Assert.assertFalse(isPageShowPersisted());
     }
 
-    // TODO: Add more cases (e.g. page eviction when in BFCache) to test
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add({"enable-features=WebViewBackForwardCache"})
+    public void testPageEvictedWhenModifyingJSInterface() throws Exception, Throwable {
+        mActivityTestRule.loadUrlSync(
+                mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
+
+        // Test adding javascript interface
+        navigateForward();
+        Object testInjectedObject =
+                new Object() {
+                    @JavascriptInterface
+                    public void mock() {}
+                };
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(
+                mAwContents, testInjectedObject, "testInjectedObject");
+        navigateBack();
+        String notRestoredReasons = getNotRestoredReasons();
+        Assert.assertEquals(extractSimpleReasonString(notRestoredReasons), "masked");
+        Assert.assertFalse(isPageShowPersisted());
+
+        // Test removing javascript interface
+        navigateForward();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> mAwContents.removeJavascriptInterface("testInjectedObject"));
+        navigateBack();
+        notRestoredReasons = getNotRestoredReasons();
+        Assert.assertEquals(extractSimpleReasonString(notRestoredReasons), "masked");
+        Assert.assertFalse(isPageShowPersisted());
+
+        // Test BFCache can still work for future navigations
+        navigateForwardAndBack();
+        Assert.assertTrue(isPageShowPersisted());
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add({"enable-features=WebViewBackForwardCache"})
+    public void testPageEvictedWhenAddingWebMessageListener() throws Exception, Throwable {
+        mActivityTestRule.loadUrlSync(
+                mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
+        navigateForward();
+        TestWebMessageListener listener = new TestWebMessageListener();
+        TestWebMessageListener.addWebMessageListenerOnUiThread(
+                mAwContents, "awMessagePort", new String[] {"*"}, listener);
+        navigateBack();
+        String notRestoredReasons = getNotRestoredReasons();
+        Assert.assertTrue(notRestoredReasons.indexOf("reasons") >= 0);
+        Assert.assertFalse(isPageShowPersisted());
+
+        // Test BFCache can still work for future navigations
+        navigateForwardAndBack();
+        Assert.assertTrue(isPageShowPersisted());
+    }
 }
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
index ecdbbe61..5674796e 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
@@ -2718,11 +2718,13 @@
     getter longitude
     getter speed
     method constructor
+    method toJSON
 interface GeolocationPosition
     attribute @@toStringTag
     getter coords
     getter timestamp
     method constructor
+    method toJSON
 interface GeolocationPositionError
     attribute @@toStringTag
     attribute PERMISSION_DENIED
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc
index 54daa5e4..c3fff2d 100644
--- a/ash/accessibility/accessibility_controller.cc
+++ b/ash/accessibility/accessibility_controller.cc
@@ -2332,6 +2332,11 @@
           base::Unretained(this)));
   if (::features::IsAccessibilityMouseKeysEnabled()) {
     pref_change_registrar_->Add(
+        prefs::kAccessibilityMouseKeysDisableInTextFields,
+        base::BindRepeating(&AccessibilityController::
+                                UpdateMouseKeysDisableInTextFieldsFromPref,
+                            base::Unretained(this)));
+    pref_change_registrar_->Add(
         prefs::kAccessibilityMouseKeysAcceleration,
         base::BindRepeating(
             &AccessibilityController::UpdateMouseKeysAccelerationFromPref,
@@ -2440,6 +2445,7 @@
   UpdateAutoclickMovementThresholdFromPref();
   UpdateAutoclickMenuPositionFromPref();
   if (::features::IsAccessibilityMouseKeysEnabled()) {
+    UpdateMouseKeysDisableInTextFieldsFromPref();
     UpdateMouseKeysAccelerationFromPref();
     UpdateMouseKeysMaxSpeedFromPref();
     UpdateMouseKeysDominantHandFromPref();
@@ -2524,6 +2530,13 @@
       GetAutoclickMenuPosition());
 }
 
+void AccessibilityController::UpdateMouseKeysDisableInTextFieldsFromPref() {
+  DCHECK(active_user_prefs_);
+  bool value = active_user_prefs_->GetBoolean(
+      prefs::kAccessibilityMouseKeysDisableInTextFields);
+  Shell::Get()->mouse_keys_controller()->set_disable_in_text_fields(value);
+}
+
 void AccessibilityController::UpdateMouseKeysAccelerationFromPref() {
   DCHECK(active_user_prefs_);
   double acceleration =
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h
index 288ca39..9d3abdda 100644
--- a/ash/accessibility/accessibility_controller.h
+++ b/ash/accessibility/accessibility_controller.h
@@ -670,6 +670,7 @@
   void UpdateAutoclickStabilizePositionFromPref();
   void UpdateAutoclickMovementThresholdFromPref();
   void UpdateAutoclickMenuPositionFromPref();
+  void UpdateMouseKeysDisableInTextFieldsFromPref();
   void UpdateMouseKeysAccelerationFromPref();
   void UpdateMouseKeysMaxSpeedFromPref();
   void UpdateMouseKeysDominantHandFromPref();
diff --git a/ash/accessibility/mouse_keys/mouse_keys_unittest.cc b/ash/accessibility/mouse_keys/mouse_keys_unittest.cc
index 34e8acb..8116cc21 100644
--- a/ash/accessibility/mouse_keys/mouse_keys_unittest.cc
+++ b/ash/accessibility/mouse_keys/mouse_keys_unittest.cc
@@ -162,7 +162,10 @@
   }
 
   void SetDisableInTextFields(bool value) {
-    return GetMouseKeysController()->set_disable_in_text_fields(value);
+    PrefService* prefs =
+        Shell::Get()->session_controller()->GetLastActiveUserPrefService();
+
+    prefs->SetBoolean(prefs::kAccessibilityMouseKeysDisableInTextFields, value);
   }
 
   void SetLeftHanded(bool value) {
diff --git a/ash/birch/birch_model.cc b/ash/birch/birch_model.cc
index 5353f2b..34adb2a 100644
--- a/ash/birch/birch_model.cc
+++ b/ash/birch/birch_model.cc
@@ -78,7 +78,7 @@
     base::UmaHistogramCounts100("Ash.Birch.ResultsReturned.Calendar",
                                 calendar_items.size());
     base::UmaHistogramTimes("Ash.Birch.Latency.Calendar",
-                            GetNow() - fetch_start_time_);
+                            GetNow() - fetch_start_time_calendar_);
     is_fetching_calendar_ = false;
   }
   if (calendar_items != calendar_items_) {
@@ -110,7 +110,7 @@
     base::UmaHistogramCounts100("Ash.Birch.ResultsReturned.File",
                                 file_suggest_items.size());
     base::UmaHistogramTimes("Ash.Birch.Latency.File",
-                            GetNow() - fetch_start_time_);
+                            GetNow() - fetch_start_time_file_suggest_);
     is_fetching_file_suggest_ = false;
   }
   if (file_suggest_items_ != file_suggest_items) {
@@ -126,7 +126,7 @@
     base::UmaHistogramCounts100("Ash.Birch.ResultsReturned.Tab",
                                 recent_tab_items.size());
     base::UmaHistogramTimes("Ash.Birch.Latency.Tab",
-                            GetNow() - fetch_start_time_);
+                            GetNow() - fetch_start_time_recent_tab_);
     is_fetching_recent_tab_ = false;
   }
   if (recent_tab_items_ != recent_tab_items) {
@@ -142,7 +142,7 @@
     base::UmaHistogramCounts100("Ash.Birch.ResultsReturned.Weather",
                                 weather_items.size());
     base::UmaHistogramTimes("Ash.Birch.Latency.Weather",
-                            GetNow() - fetch_start_time_);
+                            GetNow() - fetch_start_time_weather_);
     is_fetching_weather_ = false;
   }
   if (weather_items_ != weather_items) {
@@ -158,7 +158,7 @@
     base::UmaHistogramCounts100("Ash.Birch.ResultsReturned.ReleaseNotes",
                                 release_notes_items.size());
     base::UmaHistogramTimes("Ash.Birch.Latency.ReleaseNotes",
-                            GetNow() - fetch_start_time_);
+                            GetNow() - fetch_start_time_release_notes_);
     is_fetching_release_notes_ = false;
   }
   if (release_notes_items != release_notes_items_) {
@@ -228,6 +228,7 @@
     if (prefs->GetBoolean(prefs::kBirchUseCalendar)) {
       is_calendar_data_fresh_ = false;
       is_attachment_data_fresh_ = false;  // Attachments use the same provider.
+      fetch_start_time_calendar_ = GetNow();
       is_fetching_calendar_ = true;
       is_fetching_attachment_ = true;
       birch_client_->GetCalendarProvider()->RequestBirchDataFetch();
@@ -235,18 +236,21 @@
     }
     if (prefs->GetBoolean(prefs::kBirchUseFileSuggest)) {
       is_files_data_fresh_ = false;
+      fetch_start_time_file_suggest_ = GetNow();
       is_fetching_file_suggest_ = true;
       birch_client_->GetFileSuggestProvider()->RequestBirchDataFetch();
       did_fetch = true;
     }
     if (prefs->GetBoolean(prefs::kBirchUseRecentTabs)) {
       is_tabs_data_fresh_ = false;
+      fetch_start_time_recent_tab_ = GetNow();
       is_fetching_recent_tab_ = true;
       birch_client_->GetRecentTabsProvider()->RequestBirchDataFetch();
       did_fetch = true;
     }
     if (prefs->GetBoolean(prefs::kBirchUseReleaseNotes)) {
       is_release_notes_data_fresh_ = false;
+      fetch_start_time_release_notes_ = GetNow();
       is_fetching_release_notes_ = true;
       birch_client_->GetReleaseNotesProvider()->RequestBirchDataFetch();
       did_fetch = true;
@@ -254,6 +258,7 @@
   }
   if (weather_provider_ && prefs->GetBoolean(prefs::kBirchUseWeather)) {
     is_weather_data_fresh_ = false;
+    fetch_start_time_weather_ = GetNow();
     is_fetching_weather_ = true;
     weather_provider_->RequestBirchDataFetch();
     did_fetch = true;
@@ -526,6 +531,15 @@
   } else {
     is_calendar_data_fresh_ = false;
     is_attachment_data_fresh_ = false;
+    // When calendar is toggled on while a fetch is in progress, perform
+    // a calendar data fetch.
+    const bool fetch_in_progress = !pending_requests_.empty();
+    if (birch_client_ && fetch_in_progress && !is_fetching_calendar_) {
+      fetch_start_time_calendar_ = GetNow();
+      is_fetching_calendar_ = true;
+      is_fetching_attachment_ = true;
+      birch_client_->GetCalendarProvider()->RequestBirchDataFetch();
+    }
   }
 }
 
@@ -535,6 +549,15 @@
     file_suggest_items_.clear();
   } else {
     is_files_data_fresh_ = false;
+
+    // When file suggest is toggled on while a fetch is in progress, perform
+    // a file suggest data fetch.
+    const bool fetch_in_progress = !pending_requests_.empty();
+    if (birch_client_ && fetch_in_progress && !is_fetching_file_suggest_) {
+      fetch_start_time_file_suggest_ = GetNow();
+      is_fetching_file_suggest_ = true;
+      birch_client_->GetFileSuggestProvider()->RequestBirchDataFetch();
+    }
   }
 }
 
@@ -544,6 +567,14 @@
     recent_tab_items_.clear();
   } else {
     is_tabs_data_fresh_ = false;
+    // When recent tabs is toggled on while a fetch is in progress, perform
+    // a tabs data fetch.
+    const bool fetch_in_progress = !pending_requests_.empty();
+    if (birch_client_ && fetch_in_progress && !is_fetching_recent_tab_) {
+      fetch_start_time_recent_tab_ = GetNow();
+      is_fetching_recent_tab_ = true;
+      birch_client_->GetRecentTabsProvider()->RequestBirchDataFetch();
+    }
   }
 }
 
@@ -553,6 +584,14 @@
     weather_items_.clear();
   } else {
     is_weather_data_fresh_ = false;
+    // When weather is toggled on while a fetch is in progress, perform
+    // a weather data fetch.
+    const bool fetch_in_progress = !pending_requests_.empty();
+    if (weather_provider_ && fetch_in_progress && !is_fetching_weather_) {
+      fetch_start_time_weather_ = GetNow();
+      is_fetching_weather_ = true;
+      weather_provider_->RequestBirchDataFetch();
+    }
   }
 }
 
@@ -562,6 +601,14 @@
     release_notes_items_.clear();
   } else {
     is_release_notes_data_fresh_ = false;
+    // When release notes is toggled on while a fetch is in progress, perform
+    // a release notes fetch.
+    const bool fetch_in_progress = !pending_requests_.empty();
+    if (birch_client_ && fetch_in_progress && !is_fetching_release_notes_) {
+      fetch_start_time_release_notes_ = GetNow();
+      is_fetching_release_notes_ = true;
+      birch_client_->GetReleaseNotesProvider()->RequestBirchDataFetch();
+    }
   }
 }
 
diff --git a/ash/birch/birch_model.h b/ash/birch/birch_model.h
index c795629c..7ca19a9 100644
--- a/ash/birch/birch_model.h
+++ b/ash/birch/birch_model.h
@@ -191,6 +191,11 @@
 
   // When the last fetch was started. Used for metrics.
   base::Time fetch_start_time_;
+  base::Time fetch_start_time_calendar_;
+  base::Time fetch_start_time_file_suggest_;
+  base::Time fetch_start_time_recent_tab_;
+  base::Time fetch_start_time_weather_;
+  base::Time fetch_start_time_release_notes_;
 
   // Which fetches are in progress. Used for metrics.
   bool is_fetching_calendar_ = false;
diff --git a/ash/birch/birch_model_unittest.cc b/ash/birch/birch_model_unittest.cc
index 29f34be..3550c4e 100644
--- a/ash/birch/birch_model_unittest.cc
+++ b/ash/birch/birch_model_unittest.cc
@@ -547,6 +547,64 @@
   EXPECT_TRUE(model->IsDataFresh());
 }
 
+TEST_F(BirchModelTest, EnablePrefsDuringFetchCausesDataFetchRequest) {
+  BirchModel* model = Shell::Get()->birch_model();
+
+  // Disable all the prefs except weather, so that a data fetch request creates
+  // a pending request.
+  PrefService* prefs =
+      Shell::Get()->session_controller()->GetPrimaryUserPrefService();
+  ASSERT_TRUE(prefs);
+  prefs->SetBoolean(prefs::kBirchUseCalendar, false);
+  prefs->SetBoolean(prefs::kBirchUseFileSuggest, false);
+  prefs->SetBoolean(prefs::kBirchUseRecentTabs, false);
+  prefs->SetBoolean(prefs::kBirchUseReleaseNotes, false);
+
+  // Request a fetch, creating a pending fetch request.
+  model->RequestBirchDataFetch(/*is_post_login=*/false, base::DoNothing());
+
+  auto& client = stub_birch_client_;
+  EXPECT_FALSE(client.calendar_provider_.did_request_birch_data_fetch_);
+  EXPECT_FALSE(client.file_suggest_provider_.did_request_birch_data_fetch_);
+  EXPECT_FALSE(client.recent_tabs_provider_.did_request_birch_data_fetch_);
+  EXPECT_FALSE(client.release_notes_provider_.did_request_birch_data_fetch_);
+
+  // Enable prefs and then expect that data fetch requests are called for each
+  // enabled data type.
+  prefs->SetBoolean(prefs::kBirchUseCalendar, true);
+  prefs->SetBoolean(prefs::kBirchUseFileSuggest, true);
+  prefs->SetBoolean(prefs::kBirchUseRecentTabs, true);
+  prefs->SetBoolean(prefs::kBirchUseReleaseNotes, true);
+  EXPECT_TRUE(client.calendar_provider_.did_request_birch_data_fetch_);
+  EXPECT_TRUE(client.file_suggest_provider_.did_request_birch_data_fetch_);
+  EXPECT_TRUE(client.recent_tabs_provider_.did_request_birch_data_fetch_);
+  EXPECT_TRUE(client.release_notes_provider_.did_request_birch_data_fetch_);
+}
+
+TEST_F(BirchModelTest, EnableWeatherPrefDuringFetchCausesDataFetchRequest) {
+  BirchModel* model = Shell::Get()->birch_model();
+
+  // Install a stub weather provider.
+  auto weather_provider = std::make_unique<StubBirchDataProvider>();
+  auto* weather_provider_ptr = weather_provider.get();
+  model->OverrideWeatherProviderForTest(std::move(weather_provider));
+
+  // Disable the weather pref.
+  PrefService* prefs =
+      Shell::Get()->session_controller()->GetPrimaryUserPrefService();
+  ASSERT_TRUE(prefs);
+  prefs->SetBoolean(prefs::kBirchUseWeather, false);
+
+  // Request a fetch, creating a pending fetch request.
+  model->RequestBirchDataFetch(/*is_post_login=*/false, base::DoNothing());
+
+  EXPECT_FALSE(weather_provider_ptr->did_request_birch_data_fetch_);
+
+  // Enable the weather pref and expect a weather data fetch.
+  prefs->SetBoolean(prefs::kBirchUseWeather, true);
+  EXPECT_TRUE(weather_provider_ptr->did_request_birch_data_fetch_);
+}
+
 // Regression test for missing attachment type check in IsDataFresh().
 TEST_F(BirchModelTest, IsDataFresh_Attachments) {
   BirchModel* model = Shell::Get()->birch_model();
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 317dbba..6fff71a 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -3064,7 +3064,7 @@
 // the churn observation check in ping.
 BASE_FEATURE(kDeviceActiveClientChurnObservationNewDeviceMetadata,
              "DeviceActiveClientChurnObservationNewDeviceMetadata",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables or disables forced reboots when DeviceScheduledReboot policy is set.
 BASE_FEATURE(kDeviceForceScheduledReboot,
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 0917af4..cb08b8f 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -176,6 +176,11 @@
 inline constexpr char kEduCoexistenceToSAcceptedVersion[] =
     "family_link_user.edu_coexistence_tos_accepted_version";
 
+// A boolean pref indicating if a PIN has been set up for on-device apps
+// parental controls.
+inline constexpr char kOnDeviceAppControlsSetupCompleted[] =
+    "on_device_app_controls.setup_completed";
+
 // A boolean pref indicating whether welcome page should be skipped in
 // in-session 'Add account' flow.
 inline constexpr char kShouldSkipInlineLoginWelcomePage[] =
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index d2d85e3..2b0adf9 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -503,116 +503,6 @@
             display_manager()->GetDisplayAt(1).bounds());
 }
 
-// Test recommended zoom factor will be applied to external display when
-// connected for the first time.
-TEST_F(DisplayManagerTest, UpdateDisplayWithUnseenExternalDisplayTest) {
-  // Set up internal display and external display.
-  const int64_t internal_display_id =
-      display::test::DisplayManagerTestApi(display_manager())
-          .SetFirstDisplayAsInternalDisplay();
-  const int external_id_1 = 10;
-  const display::ManagedDisplayInfo internal_display_info =
-      CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 1920, 1200));
-  display::ManagedDisplayInfo external_display_info_1 =
-      CreateDisplayInfo(external_id_1, gfx::Rect(1, 1, 3840, 2160));
-  const float external_display_dpi_1 = 192.f;
-  external_display_info_1.set_device_dpi(external_display_dpi_1);
-
-  std::vector<display::ManagedDisplayInfo> display_info_list;
-  display_info_list.clear();
-  display_info_list.push_back(internal_display_info);
-  display_info_list.push_back(external_display_info_1);
-  display_manager()->OnNativeDisplaysChanged(display_info_list);
-
-  EXPECT_EQ(2U, display_manager()->GetNumDisplays());
-  EXPECT_EQ(2U, display_manager()->num_connected_displays());
-
-  // The recommended zoom factor should be applied since this external display
-  // is connected for the first time.
-
-  // The available zoom factors for 3840X2160 are
-  // {1.f, 1.10f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f, 2.40f}. The expected
-  // zoom factor = external_display_dpi_1 /
-  // kRecommendedDefaultExternalDisplayDpi = 192 / 96 = 2, which is available.
-  const float expect_zoom_factor_1 = 2.f;
-  EXPECT_EQ(expect_zoom_factor_1,
-            GetDisplayInfoForId(external_id_1).zoom_factor());
-
-  // Update the external display again.
-  external_display_info_1.set_device_dpi(300.f);
-  display_info_list.clear();
-  display_info_list.push_back(internal_display_info);
-  display_info_list.push_back(external_display_info_1);
-  display_manager()->OnNativeDisplaysChanged(display_info_list);
-
-  // The recommended zoom factor should not be applied since this external
-  // display is connected before.
-  EXPECT_EQ(1.f, GetDisplayInfoForId(external_id_1).zoom_factor());
-
-  // Test with a new external display with different display dpi.
-  const int external_id_2 = 20;
-  display::ManagedDisplayInfo external_display_info_2 =
-      CreateDisplayInfo(external_id_2, gfx::Rect(1, 1, 3840, 2160));
-  const float external_display_dpi_2 = 140.f;
-  external_display_info_2.set_device_dpi(external_display_dpi_2);
-
-  display_info_list.clear();
-  display_info_list.push_back(internal_display_info);
-  display_info_list.push_back(external_display_info_2);
-  display_manager()->OnNativeDisplaysChanged(display_info_list);
-
-  // The available zoom factors for 3840X2160 are
-  // {1.f, 1.10f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f, 2.40f}. The expected
-  // zoom factor = external_display_dpi_2 /
-  // kRecommendedDefaultExternalDisplayDpi = 140 / 96 = 1.46, the closest
-  // available zoom factor is 1.4.
-  const float expect_zoom_factor_2 = 1.4f;
-  EXPECT_EQ(expect_zoom_factor_2,
-            GetDisplayInfoForId(external_id_2).zoom_factor());
-
-  // Test with a new external display with a large display dpi.
-  const int external_id_3 = 30;
-  display::ManagedDisplayInfo external_display_info_3 =
-      CreateDisplayInfo(external_id_3, gfx::Rect(1, 1, 3840, 2160));
-  const float external_display_dpi_3 = 300.f;
-  external_display_info_3.set_device_dpi(external_display_dpi_3);
-
-  display_info_list.clear();
-  display_info_list.push_back(internal_display_info);
-  display_info_list.push_back(external_display_info_3);
-  display_manager()->OnNativeDisplaysChanged(display_info_list);
-
-  // The available zoom factors for 3840X2160 are
-  // {1.f, 1.10f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f, 2.40f}. The expected
-  // zoom factor = external_display_dpi_3 /
-  // kRecommendedDefaultExternalDisplayDpi = 300 / 96 = 3.125, the closest
-  // available zoom factor is 2.4.
-  const float expect_zoom_factor_3 = 2.4f;
-  EXPECT_EQ(expect_zoom_factor_3,
-            GetDisplayInfoForId(external_id_3).zoom_factor());
-
-  // Test with a new external display with a small display dpi.
-  const int external_id_4 = 40;
-  display::ManagedDisplayInfo external_display_info_4 =
-      CreateDisplayInfo(external_id_4, gfx::Rect(1, 1, 3840, 2160));
-  const float external_display_dpi_4 = 50.f;
-  external_display_info_4.set_device_dpi(external_display_dpi_4);
-
-  display_info_list.clear();
-  display_info_list.push_back(internal_display_info);
-  display_info_list.push_back(external_display_info_4);
-  display_manager()->OnNativeDisplaysChanged(display_info_list);
-
-  // The available zoom factors for 3840X2160 are
-  // {1.f, 1.10f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f, 2.40f}. The expected
-  // zoom factor = external_display_dpi_4 /
-  // kRecommendedDefaultExternalDisplayDpi = 50 / 96 = 0.52, the closest
-  // available zoom factor is 1.
-  const float expect_zoom_factor_4 = 1.f;
-  EXPECT_EQ(expect_zoom_factor_4,
-            GetDisplayInfoForId(external_id_4).zoom_factor());
-}
-
 // Test in emulation mode (use_fullscreen_host_window=false)
 TEST_F(DisplayManagerTest, EmulatorTest) {
   EXPECT_EQ(1U, display_manager()->GetNumDisplays());
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index ba256dae..e19edd6 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -75,7 +75,6 @@
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
   // DisplayObserver:
-  void OnDisplayTabletStateChanged(display::TabletState state) override;
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
 
@@ -85,6 +84,10 @@
   // AppListControllerObserver:
   void OnAppListVisibilityWillChange(bool shown, int64_t display_id) override;
 
+  // Updates the shelf configuration to match the provided tablet mode state.
+  // Called during transitions to enter or exit tablet mode.
+  void UpdateForTabletMode(bool in_tablet_mode);
+
   // Whether the shelf control buttons must be shown for accessibility
   // reasons.
   bool ShelfControlsForcedShownForAccessibility() const;
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index edbfcf2..61dc446 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -212,36 +212,11 @@
   UpdateConfig(is_app_list_visible_, /*tablet_mode_changed=*/false);
 }
 
-void ShelfConfig::OnDisplayTabletStateChanged(display::TabletState state) {
-  switch (state) {
-    case display::TabletState::kInClamshellMode:
-      break;
-    case display::TabletState::kEnteringTabletMode:
-      // Update the shelf config at the "starting" stage of the tablet mode
-      // transition, so that the shelf bounds are set and remains stable during
-      // the transition animation. Otherwise, updating the shelf bounds during
-      // the animation will lead to work-area bounds changes which lead to many
-      // re-layouts, hurting the animation's smoothness.
-      // https://crbug.com/1044316.
-      DCHECK(!in_tablet_mode_);
-      in_tablet_mode_ = true;
-
-      UpdateConfig(is_app_list_visible_, /*tablet_mode_changed=*/true);
-      break;
-    case display::TabletState::kInTabletMode:
-      break;
-    case display::TabletState::kExitingTabletMode:
-      // Many events can lead to UpdateConfig being called as a result of
-      // kInClamshellMode event, therefore we need to listen to the "ending"
-      // stage rather than the "ended", so `in_tablet_mode_` gets updated
-      // correctly, and the shelf bounds are stabilized early so as not to have
-      // multiple unnecessary work-area bounds changes.
-      in_tablet_mode_ = false;
-
-      UpdateConfig(is_app_list_visible_, /*tablet_mode_changed=*/true);
-
-      has_shown_elevated_app_bar_ = std::nullopt;
-      break;
+void ShelfConfig::UpdateForTabletMode(bool in_tablet_mode) {
+  in_tablet_mode_ = in_tablet_mode;
+  UpdateConfig(is_app_list_visible_, /*tablet_mode_changed=*/true);
+  if (!in_tablet_mode_) {
+    has_shown_elevated_app_bar_ = std::nullopt;
   }
 }
 
diff --git a/ash/system/toast/system_nudge_view.cc b/ash/system/toast/system_nudge_view.cc
index 119f592..21f2e446 100644
--- a/ash/system/toast/system_nudge_view.cc
+++ b/ash/system/toast/system_nudge_view.cc
@@ -34,6 +34,7 @@
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_view.h"
 #include "ui/views/view.h"
+#include "ui/views/view_class_properties.h"
 #include "ui/views/view_tracker.h"
 
 namespace ash {
@@ -122,6 +123,12 @@
 
 }  // namespace
 
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(SystemNudgeView, kBubbleIdForTesting);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(SystemNudgeView,
+                                      kPrimaryButtonIdForTesting);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(SystemNudgeView,
+                                      kSecondaryButtonIdForTesting);
+
 SystemNudgeView::SystemNudgeView(const AnchoredNudgeData& nudge_data)
     : shadow_(SystemShadow::CreateShadowOnTextureLayer(
           SystemShadow::Type::kElevation4)),
@@ -135,6 +142,7 @@
   SetBackground(views::CreateThemedSolidBackground(
       nudge_data.background_color_id.value_or(kColorAshShieldAndBase80)));
   SetNotifyEnterExitOnChild(true);
+  SetProperty(views::kElementIdentifierKey, kBubbleIdForTesting);
 
   // Cache the anchor view when the nudge anchors by its corner to set a pointy
   // corner based on the nudge's position in relation to this anchor view.
@@ -340,6 +348,7 @@
           .SetTooltipText(nudge_data.primary_button_text)
           .SetPillButtonType(PillButton::Type::kPrimaryWithoutIcon)
           .SetFocusBehavior(views::View::FocusBehavior::ALWAYS)
+          .SetProperty(views::kElementIdentifierKey, kPrimaryButtonIdForTesting)
           .Build());
 
   if (has_secondary_button) {
@@ -351,6 +360,8 @@
             .SetTooltipText(nudge_data.secondary_button_text)
             .SetPillButtonType(PillButton::Type::kSecondaryWithoutIcon)
             .SetFocusBehavior(views::View::FocusBehavior::ALWAYS)
+            .SetProperty(views::kElementIdentifierKey,
+                         kSecondaryButtonIdForTesting)
             .Build(),
         0);
   }
diff --git a/ash/system/toast/system_nudge_view.h b/ash/system/toast/system_nudge_view.h
index 984f409d..0bc6b0a 100644
--- a/ash/system/toast/system_nudge_view.h
+++ b/ash/system/toast/system_nudge_view.h
@@ -35,6 +35,10 @@
   METADATA_HEADER(SystemNudgeView, views::FlexLayoutView)
 
  public:
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kBubbleIdForTesting);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kPrimaryButtonIdForTesting);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kSecondaryButtonIdForTesting);
+
   SystemNudgeView(const AnchoredNudgeData& nudge_data);
   SystemNudgeView(const SystemNudgeView&) = delete;
   SystemNudgeView& operator=(const SystemNudgeView&) = delete;
diff --git a/ash/webui/personalization_app/resources/js/user/avatar_list_element.ts b/ash/webui/personalization_app/resources/js/user/avatar_list_element.ts
index d0528760..1a17a9c 100644
--- a/ash/webui/personalization_app/resources/js/user/avatar_list_element.ts
+++ b/ash/webui/personalization_app/resources/js/user/avatar_list_element.ts
@@ -429,7 +429,8 @@
       assert(
           !url.startsWith('chrome://image/'),
           'The URL shouldn\'t be sanitized');
-      return `background-image: url('${getAvatarUrl(url)}&staticEncode=true')`;
+      return `background-image: url('${
+          getAvatarUrl(url, /*staticEncode=*/ true)}')`;
     }
     return '';
   }
diff --git a/ash/webui/personalization_app/resources/js/user/user_preview_element.ts b/ash/webui/personalization_app/resources/js/user/user_preview_element.ts
index f201e3a..897792c1f 100644
--- a/ash/webui/personalization_app/resources/js/user/user_preview_element.ts
+++ b/ash/webui/personalization_app/resources/js/user/user_preview_element.ts
@@ -171,7 +171,8 @@
     }
     assert(
         !url.startsWith('chrome://image/'), 'The url should not be sanitized');
-    return `background-image: url('${getAvatarUrl(url)}&staticEncode=true')`;
+    return `background-image: url('${
+        getAvatarUrl(url, /*staticEncode=*/ true)}')`;
   }
 
   private getAvatarUrl_(url: string): string {
diff --git a/ash/webui/personalization_app/resources/js/user/utils.ts b/ash/webui/personalization_app/resources/js/user/utils.ts
index 8ca972b..25ba7a4 100644
--- a/ash/webui/personalization_app/resources/js/user/utils.ts
+++ b/ash/webui/personalization_app/resources/js/user/utils.ts
@@ -14,7 +14,8 @@
  * Returns the avatar url. If necessary, prefixes the url with the sanitizing
  * string.
  */
-export function getAvatarUrl(url: string): string {
+export function getAvatarUrl(
+    url: string, staticEncode: boolean = false): string {
   if (!url) {
     return '';
   }
@@ -22,5 +23,8 @@
       url === AVATAR_PLACEHOLDER_URL) {
     return url;
   }
-  return `chrome://image/?${url}`;
+  if (!staticEncode) {
+    return `chrome://image/?${url}`;
+  }
+  return `chrome://image/?url=${encodeURIComponent(url)}&staticEncode=true`;
 }
diff --git a/ash/wm/default_window_resizer.h b/ash/wm/default_window_resizer.h
index 5dc7df00..5d5ca1a 100644
--- a/ash/wm/default_window_resizer.h
+++ b/ash/wm/default_window_resizer.h
@@ -12,9 +12,9 @@
 
 namespace ash {
 
-// WindowResizer is used by ToplevelWindowEventFilter to handle dragging, moving
-// or resizing a window. All coordinates passed to this are in the parent
-// windows coordiantes.
+// WindowResizer is used by ToplevelWindowEventHandler to handle dragging,
+// moving or resizing a window. All coordinates passed to this are in the parent
+// windows coordinates.
 class ASH_EXPORT DefaultWindowResizer : public WindowResizer {
  public:
   ~DefaultWindowResizer() override;
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index dd479eb..4aa6391 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -908,6 +908,13 @@
     Shell::Get()->display_manager()->SetTabletState(
         display::TabletState::kExitingTabletMode);
 
+    // Many events can lead to shelf config updates as a result of
+    // kInClamshellMode event. Update the shelf config during "ending"
+    // stage rather than the "ended", so `in_tablet_mode_` gets updated
+    // correctly, and the shelf bounds are stabilized early so as not to have
+    // multiple unnecessary work-area bounds changes.
+    ShelfConfig::Get()->UpdateForTabletMode(/*in_tablet_mode=*/false);
+
     if (tablet_mode_window_manager_) {
       tablet_mode_window_manager_->Shutdown(
           TabletModeWindowManager::ShutdownReason::kExitTabletUIMode);
@@ -1167,6 +1174,18 @@
   DCHECK_EQ(display::TabletState::kEnteringTabletMode,
             display::Screen::GetScreen()->GetTabletState());
 
+  // Transition shelf to tablet mode state, now that the screenshot for tablet
+  // mode transition was taken. Taking screenshot recreates shelf container
+  // layer, and uses the original layer - changing shelf state before the
+  // screenshot is taken would change the shelf appearance, and could cause
+  // issues where the original shelf widget layer is not re-painted correctly in
+  // response to a paint schedule for tablet mode state change.
+  // Update the shelf state befire initiating tablet mode window state changes
+  // to avoid negative impact of window work-area changes (due to changes in
+  // shelf bounds) during window state transition on the animation smoothness
+  // https://crbug.com/1044316.
+  ShelfConfig::Get()->UpdateForTabletMode(/*in_tablet_mode=*/true);
+
   tablet_mode_window_manager_ = std::make_unique<TabletModeWindowManager>();
   tablet_mode_window_manager_->Init();
 
diff --git a/ash/wm/window_resizer.cc b/ash/wm/window_resizer.cc
index 0b011209..b120e4d3 100644
--- a/ash/wm/window_resizer.cc
+++ b/ash/wm/window_resizer.cc
@@ -170,7 +170,7 @@
   // The minimize size constraint may limit how much we change the window
   // position.  For example, dragging the left edge to the right should stop
   // repositioning the window when the minimize size is reached.
-  gfx::Size size = GetSizeForDrag(&delta_x, &delta_y);
+  const gfx::Size size = GetSizeForDrag(&delta_x, &delta_y);
   gfx::Point origin = GetOriginForDrag(delta_x, delta_y, passed_location);
   gfx::Rect new_bounds(origin, size);
 
@@ -417,12 +417,12 @@
   return origin;
 }
 
-gfx::Size WindowResizer::GetSizeForDrag(int* delta_x, int* delta_y) {
+gfx::Size WindowResizer::GetSizeForDrag(int* delta_x, int* delta_y) const {
   gfx::Size size = details().initial_bounds_in_parent.size();
   if (details().bounds_change & kBoundsChange_Resizes) {
-    gfx::Size min_size = GetTarget()->delegate()
-                             ? GetTarget()->delegate()->GetMinimumSize()
-                             : gfx::Size();
+    const gfx::Size min_size = GetTarget()->delegate()
+                                   ? GetTarget()->delegate()->GetMinimumSize()
+                                   : gfx::Size();
     size.SetSize(GetWidthForDrag(min_size.width(), delta_x),
                  GetHeightForDrag(min_size.height(), delta_y));
   } else if (!details().restore_bounds_in_parent.IsEmpty() &&
@@ -434,7 +434,7 @@
   return size;
 }
 
-int WindowResizer::GetWidthForDrag(int min_width, int* delta_x) {
+int WindowResizer::GetWidthForDrag(int min_width, int* delta_x) const {
   int width = details().initial_bounds_in_parent.width();
   if (details().size_change_direction & kBoundsChangeDirection_Horizontal) {
     // Along the right edge, positive delta_x increases the window size.
@@ -468,7 +468,7 @@
   return width;
 }
 
-int WindowResizer::GetHeightForDrag(int min_height, int* delta_y) {
+int WindowResizer::GetHeightForDrag(int min_height, int* delta_y) const {
   int height = details().initial_bounds_in_parent.height();
   if (details().size_change_direction & kBoundsChangeDirection_Vertical) {
     // Along the bottom edge, positive delta_y increases the window size.
diff --git a/ash/wm/window_resizer.h b/ash/wm/window_resizer.h
index 38d699b..7470d44c 100644
--- a/ash/wm/window_resizer.h
+++ b/ash/wm/window_resizer.h
@@ -29,8 +29,8 @@
 namespace ash {
 class PresentationTimeRecorder;
 
-// WindowResizer is used by ToplevelWindowEventFilter to handle dragging, moving
-// or resizing a window. All coordinates passed to this are in the parent
+// WindowResizer is used by ToplevelWindowEventHandler to handle dragging,
+// moving or resizing a window. All coordinates passed to this are in the parent
 // windows coordinates.
 class ASH_EXPORT WindowResizer {
  public:
@@ -51,10 +51,10 @@
 
   virtual ~WindowResizer();
 
-  // Returns a bitmask of the kBoundsChange_ values.
+  // Returns a bitmask of the `kBoundsChange_*` values.
   static int GetBoundsChangeForWindowComponent(int component);
 
-  // Returns a bitmask of the kBoundsChange_ values.
+  // Returns a bitmask of the `kBoundsChangeDirection_*` values.
   static int GetPositionChangeDirectionForWindowComponent(int window_component);
 
   // Invoked to drag/move/resize the window. |location| is in the coordinates
@@ -117,13 +117,13 @@
                               const gfx::PointF& event_location);
 
   // Returns the size of the window for the drag.
-  gfx::Size GetSizeForDrag(int* delta_x, int* delta_y);
+  gfx::Size GetSizeForDrag(int* delta_x, int* delta_y) const;
 
-  // Returns the width of the window.
-  int GetWidthForDrag(int min_width, int* delta_x);
+  // Called by `GetSizeForDrag` to get the width of the window for the drag.
+  int GetWidthForDrag(int min_width, int* delta_x) const;
 
-  // Returns the height of the drag.
-  int GetHeightForDrag(int min_height, int* delta_y);
+  // Called by `GetSizeForDrag` to get the height of the window for the drag.
+  int GetHeightForDrag(int min_height, int* delta_y) const;
 
   // Updates |new_bounds| to adhere to the aspect ratio.
   void CalculateBoundsWithAspectRatio(float aspect_ratio,
diff --git a/base/base64.cc b/base/base64.cc
index 660ab8d..c877aaa5 100644
--- a/base/base64.cc
+++ b/base/base64.cc
@@ -6,6 +6,8 @@
 
 #include <stddef.h>
 
+#include <string_view>
+
 #include "base/check.h"
 #include "base/numerics/checked_math.h"
 #include "base/strings/string_util.h"
@@ -46,11 +48,11 @@
   CHECK_EQ(output->size(), prefix_len + output_size);
 }
 
-std::string Base64Encode(StringPiece input) {
+std::string Base64Encode(std::string_view input) {
   return Base64Encode(base::as_byte_span(input));
 }
 
-bool Base64Decode(StringPiece input,
+bool Base64Decode(std::string_view input,
                   std::string* output,
                   Base64DecodePolicy policy) {
   std::string temp;
@@ -87,7 +89,7 @@
   return true;
 }
 
-std::optional<std::vector<uint8_t>> Base64Decode(StringPiece input) {
+std::optional<std::vector<uint8_t>> Base64Decode(std::string_view input) {
   std::vector<uint8_t> ret(modp_b64_decode_len(input.size()));
 
   size_t input_size = input.size();
diff --git a/base/base64.h b/base/base64.h
index 3ebf60c..ce2d7c04 100644
--- a/base/base64.h
+++ b/base/base64.h
@@ -9,11 +9,11 @@
 
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/base_export.h"
 #include "base/containers/span.h"
-#include "base/strings/string_piece.h"
 
 namespace base {
 
@@ -25,7 +25,7 @@
                                     std::string* output);
 
 // Encodes the input string in base64.
-BASE_EXPORT std::string Base64Encode(StringPiece input);
+BASE_EXPORT std::string Base64Encode(std::string_view input);
 
 // Decodes the base64 input string.  Returns true if successful and false
 // otherwise. The output string is only modified if successful. The decoding can
@@ -44,12 +44,13 @@
   kForgiving,
 };
 BASE_EXPORT bool Base64Decode(
-    StringPiece input,
+    std::string_view input,
     std::string* output,
     Base64DecodePolicy policy = Base64DecodePolicy::kStrict);
 
 // Decodes the base64 input string. Returns `std::nullopt` if unsuccessful.
-BASE_EXPORT std::optional<std::vector<uint8_t>> Base64Decode(StringPiece input);
+BASE_EXPORT std::optional<std::vector<uint8_t>> Base64Decode(
+    std::string_view input);
 
 }  // namespace base
 
diff --git a/base/base64_decode_fuzzer.cc b/base/base64_decode_fuzzer.cc
index 557caf8..16a16a330 100644
--- a/base/base64_decode_fuzzer.cc
+++ b/base/base64_decode_fuzzer.cc
@@ -3,13 +3,13 @@
 // found in the LICENSE file.
 
 #include <string>
+#include <string_view>
 
 #include "base/base64.h"
-#include "base/strings/string_piece.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   std::string decode_output;
-  base::StringPiece data_piece(reinterpret_cast<const char*>(data), size);
+  std::string_view data_piece(reinterpret_cast<const char*>(data), size);
   base::Base64Decode(data_piece, &decode_output);
   return 0;
 }
diff --git a/base/base64_encode_fuzzer.cc b/base/base64_encode_fuzzer.cc
index bda3989..969a80f 100644
--- a/base/base64_encode_fuzzer.cc
+++ b/base/base64_encode_fuzzer.cc
@@ -3,22 +3,23 @@
 // found in the LICENSE file.
 
 #include <string>
+#include <string_view>
 
 #include "base/base64.h"
 #include "base/check_op.h"
-#include "base/strings/string_piece.h"
 
 // Encode some random data, and then decode it.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   base::span<const uint8_t> data_span(data, size);
-  base::StringPiece data_piece(reinterpret_cast<const char*>(data), size);
+  std::string_view data_piece(reinterpret_cast<const char*>(data), size);
 
   const std::string encode_output = base::Base64Encode(data_span);
   std::string decode_output;
   CHECK(base::Base64Decode(encode_output, &decode_output));
   CHECK_EQ(data_piece, decode_output);
 
-  // Also run the StringPiece variant and check that it gives the same results.
+  // Also run the std::string_view variant and check that it gives the same
+  // results.
   CHECK_EQ(encode_output, base::Base64Encode(data_piece));
 
   return 0;
diff --git a/base/base64_unittest.cc b/base/base64_unittest.cc
index b8705e6..1b110c8e 100644
--- a/base/base64_unittest.cc
+++ b/base/base64_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "base/base64.h"
 
+#include <string_view>
+
 #include "base/numerics/checked_math.h"
 #include "base/strings/escape.h"
 #include "base/test/gtest_util.h"
@@ -99,10 +101,10 @@
 
   std::string binary_encoded = Base64Encode(kData);
 
-  // Check that encoding the same data through the StringPiece interface gives
-  // the same results.
+  // Check that encoding the same data through the std::string_view interface
+  // gives the same results.
   std::string string_piece_encoded = Base64Encode(
-      StringPiece(reinterpret_cast<const char*>(kData), sizeof(kData)));
+      std::string_view(reinterpret_cast<const char*>(kData), sizeof(kData)));
 
   EXPECT_EQ(binary_encoded, string_piece_encoded);
 
diff --git a/base/base64url.cc b/base/base64url.cc
index daf91ff..b68a83ab 100644
--- a/base/base64url.cc
+++ b/base/base64url.cc
@@ -6,6 +6,8 @@
 
 #include <stddef.h>
 
+#include <string_view>
+
 #include "base/base64.h"
 #include "base/numerics/safe_math.h"
 #include "base/strings/string_util.h"
@@ -22,12 +24,12 @@
 const char kBase64Chars[] = "+/";
 const char kBase64UrlSafeChars[] = "-_";
 
-class StringPieceOrString {
+class StringViewOrString {
  public:
-  explicit StringPieceOrString(StringPiece piece) : piece_(piece) {}
-  explicit StringPieceOrString(std::string str) : str_(std::move(str)) {}
+  explicit StringViewOrString(std::string_view piece) : piece_(piece) {}
+  explicit StringViewOrString(std::string str) : str_(std::move(str)) {}
 
-  StringPiece get() const {
+  std::string_view get() const {
     if (str_) {
       return *str_;
     }
@@ -36,12 +38,12 @@
 
  private:
   const std::optional<std::string> str_;
-  const StringPiece piece_;
+  const std::string_view piece_;
 };
 
 // Converts the base64url `input` into a plain base64 string.
-std::optional<StringPieceOrString> Base64ToBase64URL(
-    StringPiece input,
+std::optional<StringViewOrString> Base64ToBase64URL(
+    std::string_view input,
     Base64UrlDecodePolicy policy) {
   // Characters outside of the base64url alphabet are disallowed, which includes
   // the {+, /} characters found in the conventional base64 alphabet.
@@ -69,7 +71,7 @@
   }
 
   if (required_padding_characters == 0 && !needs_replacement) {
-    return StringPieceOrString(input);
+    return StringViewOrString(input);
   }
 
   // If the string either needs replacement of URL-safe characters to normal
@@ -91,7 +93,7 @@
   // Append the necessary padding characters.
   base64_input.resize(out_size.ValueOrDie(), '=');
 
-  return StringPieceOrString(std::move(base64_input));
+  return StringViewOrString(std::move(base64_input));
 }
 
 }  // namespace
@@ -120,16 +122,16 @@
   }
 }
 
-void Base64UrlEncode(StringPiece input,
+void Base64UrlEncode(std::string_view input,
                      Base64UrlEncodePolicy policy,
                      std::string* output) {
   Base64UrlEncode(base::as_byte_span(input), policy, output);
 }
 
-bool Base64UrlDecode(StringPiece input,
+bool Base64UrlDecode(std::string_view input,
                      Base64UrlDecodePolicy policy,
                      std::string* output) {
-  std::optional<StringPieceOrString> base64_input =
+  std::optional<StringViewOrString> base64_input =
       Base64ToBase64URL(input, policy);
   if (!base64_input) {
     return false;
@@ -138,9 +140,9 @@
 }
 
 std::optional<std::vector<uint8_t>> Base64UrlDecode(
-    StringPiece input,
+    std::string_view input,
     Base64UrlDecodePolicy policy) {
-  std::optional<StringPieceOrString> base64_input =
+  std::optional<StringViewOrString> base64_input =
       Base64ToBase64URL(input, policy);
   if (!base64_input) {
     return std::nullopt;
diff --git a/base/base64url.h b/base/base64url.h
index 6fa0484..b18a6d58 100644
--- a/base/base64url.h
+++ b/base/base64url.h
@@ -7,11 +7,11 @@
 
 #include <optional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/base_export.h"
 #include "base/containers/span.h"
-#include "base/strings/string_piece.h"
 
 namespace base {
 
@@ -33,7 +33,7 @@
                                  std::string* output);
 
 // Same as the previous function, but accepts an input string.
-BASE_EXPORT void Base64UrlEncode(StringPiece input,
+BASE_EXPORT void Base64UrlEncode(std::string_view input,
                                  Base64UrlEncodePolicy policy,
                                  std::string* output);
 
@@ -53,13 +53,13 @@
 //
 // The |policy| defines whether padding will be required, ignored or disallowed
 // altogether. |input| and |*output| may reference the same storage.
-[[nodiscard]] BASE_EXPORT bool Base64UrlDecode(StringPiece input,
+[[nodiscard]] BASE_EXPORT bool Base64UrlDecode(std::string_view input,
                                                Base64UrlDecodePolicy policy,
                                                std::string* output);
 
 // Same as the previous function, but writing to a `std::vector`.
 [[nodiscard]] BASE_EXPORT std::optional<std::vector<uint8_t>> Base64UrlDecode(
-    StringPiece input,
+    std::string_view input,
     Base64UrlDecodePolicy policy);
 
 }  // namespace base
diff --git a/base/base64url_unittest.cc b/base/base64url_unittest.cc
index ddf072d5..30ee347 100644
--- a/base/base64url_unittest.cc
+++ b/base/base64url_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "base/base64url.h"
 
+#include <string_view>
+
 #include "base/ranges/algorithm.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,11 +24,11 @@
   Base64UrlEncode(kData, Base64UrlEncodePolicy::INCLUDE_PADDING,
                   &binary_encoded_with_padding);
 
-  // Check that encoding the same binary data through the StringPiece interface
-  // gives the same result.
+  // Check that encoding the same binary data through the std::string_view
+  // interface gives the same result.
   std::string string_encoded_with_padding;
   Base64UrlEncode(
-      StringPiece(reinterpret_cast<const char*>(kData), sizeof(kData)),
+      std::string_view(reinterpret_cast<const char*>(kData), sizeof(kData)),
       Base64UrlEncodePolicy::INCLUDE_PADDING, &string_encoded_with_padding);
   EXPECT_EQ(binary_encoded_with_padding, string_encoded_with_padding);
 
@@ -51,11 +53,11 @@
   Base64UrlEncode(kData, Base64UrlEncodePolicy::OMIT_PADDING,
                   &binary_encoded_without_padding);
 
-  // Check that encoding the same binary data through the StringPiece interface
-  // gives the same result.
+  // Check that encoding the same binary data through the std::string_view
+  // interface gives the same result.
   std::string string_encoded_without_padding;
   Base64UrlEncode(
-      StringPiece(reinterpret_cast<const char*>(kData), sizeof(kData)),
+      std::string_view(reinterpret_cast<const char*>(kData), sizeof(kData)),
       Base64UrlEncodePolicy::OMIT_PADDING, &string_encoded_without_padding);
   EXPECT_EQ(binary_encoded_without_padding, string_encoded_without_padding);
 
diff --git a/base/check_is_test.cc b/base/check_is_test.cc
index da9cad8e..1479fac 100644
--- a/base/check_is_test.cc
+++ b/base/check_is_test.cc
@@ -15,6 +15,11 @@
 void check_is_test_impl(base::NotFatalUntil fatal_milestone) {
   CHECK(g_this_is_a_test, fatal_milestone);
 }
+
+// static
+bool IsInTest::Get() {
+  return g_this_is_a_test;
+}
 }  // namespace base::internal
 
 namespace base::test {
@@ -23,7 +28,7 @@
 // code. We therefore have to also mark the symbol as exported here.
 BASE_EXPORT void AllowCheckIsTestForTesting() {
   // This CHECK ensures that `AllowCheckIsTestForTesting` is called
-  // just once. Since it is called in `base::TestSuite`, this should effectivly
+  // just once. Since it is called in `base::TestSuite`, this should effectively
   // prevent calls to `AllowCheckIsTestForTesting` in production code
   // (assuming that code has unit test coverage).
   //
@@ -34,5 +39,4 @@
 
   g_this_is_a_test = true;
 }
-
 }  // namespace base::test
diff --git a/base/check_is_test.h b/base/check_is_test.h
index f53ed9d4..074b7022 100644
--- a/base/check_is_test.h
+++ b/base/check_is_test.h
@@ -6,8 +6,13 @@
 #define BASE_CHECK_IS_TEST_H_
 
 #include "base/base_export.h"
+#include "base/check.h"
 #include "base/not_fatal_until.h"
 
+namespace web_app {
+class OsIntegrationTestOverride;
+}
+
 // Code paths taken in tests are sometimes different from those taken in
 // production. This might be because the respective tests do not initialize some
 // objects that would be required for the "normal" code path.
@@ -45,6 +50,18 @@
 BASE_EXPORT void check_is_test_impl(
     base::NotFatalUntil fatal_milestone =
         base::NotFatalUntil::NoSpecifiedMilestoneInternal);
+
+// This class facilitates an allow-list for GetIsInTest(), helping prevent
+// possible misuse.
+class BASE_EXPORT IsInTest {
+ private:
+  friend class web_app::OsIntegrationTestOverride;
+  static bool Get();
+};
 }  // namespace base::internal
 
+// In special cases, code should not execute in a test. This functionality is
+// protected by the list of friends in `IsInTest`.
+#define CHECK_IS_NOT_TEST() CHECK(!base::internal::IsInTest::Get())
+
 #endif  // BASE_CHECK_IS_TEST_H_
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorFaviconProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorFaviconProvider.java
index 583527ca..12077ae7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorFaviconProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorFaviconProvider.java
@@ -22,6 +22,7 @@
     static final int TAB_GROUP_FAVICON_COLOR_LEVEL = 1;
     static final int FAVICON_BACKGROUND_DEFAULT_ALPHA = 255;
     static final int FAVICON_BACKGROUND_SELECTED_ALPHA = 0;
+    private static final int INVALID_COLOR_ID = -1;
     private final Context mContext;
 
     public TabGroupColorFaviconProvider(Context context) {
@@ -74,20 +75,29 @@
         return new TabFaviconFetcher() {
             @Override
             public void fetch(Callback<TabFavicon> faviconCallback) {
-                final @ColorInt int color =
-                        ColorPickerUtils.getTabGroupColorPickerItemColor(
-                                mContext, colorId, isIncognito);
+                if (colorId != INVALID_COLOR_ID) {
+                    final @ColorInt int color =
+                            ColorPickerUtils.getTabGroupColorPickerItemColor(
+                                    mContext, colorId, isIncognito);
 
-                LayerDrawable tabGroupColorIcon =
-                        (LayerDrawable)
-                                ResourcesCompat.getDrawable(
-                                        mContext.getResources(),
-                                        org.chromium.chrome.tab_ui.R.drawable.tab_group_color_icon,
-                                        mContext.getTheme());
-                ((GradientDrawable) tabGroupColorIcon.getDrawable(TAB_GROUP_FAVICON_COLOR_LEVEL))
-                        .setColor(color);
+                    LayerDrawable tabGroupColorIcon =
+                            (LayerDrawable)
+                                    ResourcesCompat.getDrawable(
+                                            mContext.getResources(),
+                                            org.chromium.chrome.tab_ui.R.drawable
+                                                    .tab_group_color_icon,
+                                            mContext.getTheme());
+                    ((GradientDrawable)
+                                    tabGroupColorIcon.getDrawable(TAB_GROUP_FAVICON_COLOR_LEVEL))
+                            .setColor(color);
 
-                faviconCallback.onResult(new TabGroupColorFavicon(tabGroupColorIcon, colorId));
+                    faviconCallback.onResult(new TabGroupColorFavicon(tabGroupColorIcon, colorId));
+                } else {
+                    // If the color is invalid, don't set a drawable.
+                    faviconCallback.onResult(
+                            new TabGroupColorFavicon(
+                                    null, org.chromium.components.tab_groups.TabGroupColorId.GREY));
+                }
             }
         };
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index f051cbe..a66d745 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -1932,12 +1932,12 @@
                                         title,
                                         numOfRelatedTabs));
             } else {
-                // In theory this colorId should never be INVALID, but we should aim to read
-                // something vs nothing if this ever fails.
-                final @TabGroupColorId int colorId =
-                        TabGroupColorUtils.getOrCreateTabGroupColor(
-                                pseudoTab.getRootId(),
-                                (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get());
+                int colorId = TabGroupColorUtils.getTabGroupColor(pseudoTab.getRootId());
+                // This should never be the case in practice, but if the color is invalid then set
+                // it to the first color in the list.
+                if (colorId == INVALID_COLOR_ID) {
+                    colorId = TabGroupColorId.GREY;
+                }
                 final @StringRes int colorDescRes =
                         ColorPickerUtils.getTabGroupColorPickerItemColorAccessibilityString(
                                 colorId);
@@ -1991,12 +1991,12 @@
                                         numOfRelatedTabs));
                     }
                 } else {
-                    // In theory this colorId should never be INVALID, but we should aim to read
-                    // something vs nothing if this ever fails.
-                    final @TabGroupColorId int colorId =
-                            TabGroupColorUtils.getOrCreateTabGroupColor(
-                                    pseudoTab.getRootId(),
-                                    (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get());
+                    int colorId = TabGroupColorUtils.getTabGroupColor(pseudoTab.getRootId());
+                    // This should never be the case in practice, but if the color is invalid then
+                    // set it to the first color in the list.
+                    if (colorId == INVALID_COLOR_ID) {
+                        colorId = TabGroupColorId.GREY;
+                    }
                     final @StringRes int colorDescRes =
                             ColorPickerUtils.getTabGroupColorPickerItemColorAccessibilityString(
                                     colorId);
@@ -2113,9 +2113,7 @@
                 if (ChromeFeatureList.sTabGroupParityAndroid.isEnabled()) {
                     TabGroupModelFilter filter =
                             (TabGroupModelFilter) mCurrentTabModelFilterSupplier.get();
-                    final @TabGroupColorId int colorId =
-                            TabGroupColorUtils.getOrCreateTabGroupColor(
-                                    pseudoTab.getRootId(), filter);
+                    int colorId = TabGroupColorUtils.getTabGroupColor(pseudoTab.getRootId());
                     faviconFetcher =
                             mTabGroupColorFaviconProvider.getFaviconFromTabGroupColorFetcher(
                                     colorId, filter.getTabModel().isIncognito());
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
index 9492bdf3..f7c1dd71 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
@@ -152,6 +152,12 @@
         clickFirstCardFromTabSwitcher(cta);
         clickNthTabInDialog(cta, 4);
 
+        ViewUtils.waitForVisibleView(
+                allOf(
+                        withId(R.id.tab_list_recycler_view),
+                        isDescendantOfA(withId(R.id.bottom_controls)),
+                        isCompletelyDisplayed()));
+
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     ViewGroup bottomToolbar = cta.findViewById(R.id.bottom_controls);
@@ -178,6 +184,12 @@
         clickFirstCardFromTabSwitcher(cta);
         clickNthTabInDialog(cta, 9);
 
+        ViewUtils.waitForVisibleView(
+                allOf(
+                        withId(R.id.tab_list_recycler_view),
+                        isDescendantOfA(withId(R.id.bottom_controls)),
+                        isCompletelyDisplayed()));
+
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     ViewGroup bottomToolbar = cta.findViewById(R.id.bottom_controls);
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilitiesUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilitiesUnitTest.java
index af775b9..b7f9ba6 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilitiesUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilitiesUnitTest.java
@@ -95,7 +95,6 @@
 
     @Test
     @Config(sdk = VERSION_CODES.S)
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     public void testIsTabDragDropEnabled() {
         MultiWindowTestUtils.enableMultiInstance();
         assertTrue(TabUiFeatureUtilities.isTabDragEnabled());
@@ -103,10 +102,7 @@
 
     @Test
     @Config(sdk = VERSION_CODES.S)
-    @EnableFeatures({
-        ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID,
-        ChromeFeatureList.TAB_DRAG_DROP_ANDROID
-    })
+    @EnableFeatures({ChromeFeatureList.TAB_DRAG_DROP_ANDROID})
     public void testIsTabDragDropEnabled_bothFlagsEnabled() {
         MultiWindowTestUtils.enableMultiInstance();
         assertThrows(AssertionError.class, () -> TabUiFeatureUtilities.isTabDragEnabled());
@@ -114,7 +110,6 @@
 
     @Test
     @Config(sdk = VERSION_CODES.Q)
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     public void testIsTabDragDropEnabled_multiInstanceDisabled() {
         assertFalse(TabUiFeatureUtilities.isTabDragEnabled());
     }
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 2cfe00a..88161051 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
@@ -30,7 +30,7 @@
 
 /** Fragment showing management options for financial accounts like Pix, e-Wallets etc. */
 public class FinancialAccountsManagementFragment extends ChromeBaseSettingsFragment
-        implements PersonalDataManagerObserver {
+        implements PersonalDataManagerObserver, Preference.OnPreferenceChangeListener {
     private static Callback<Fragment> sObserverForTest;
 
     @VisibleForTesting static final String PREFERENCE_KEY_PIX = "pix";
@@ -39,6 +39,7 @@
     static final String TITLE_KEY = "financial_accounts_management_title";
 
     private PersonalDataManager mPersonalDataManager;
+    private BankAccount[] mBankAccounts;
 
     // ChromeBaseSettingsFramgent override.
     @Override
@@ -89,18 +90,45 @@
         getPreferenceScreen().removeAll();
         getPreferenceScreen().setOrderingAsAdded(true);
 
-        BankAccount[] bankAccounts = mPersonalDataManager.getMaskedBankAccounts();
-        if (bankAccounts.length > 0) {
-            ChromeSwitchPreference pixSwitch = new ChromeSwitchPreference(getStyledContext());
-            pixSwitch.setKey(PREFERENCE_KEY_PIX);
-            pixSwitch.setTitle(R.string.settings_manage_other_financial_accounts_pix);
-            getPreferenceScreen().addPreference(pixSwitch);
-            for (BankAccount bankAccount : bankAccounts) {
-                getPreferenceScreen().addPreference(getPreferenceForBankAccount(bankAccount));
+        mBankAccounts = mPersonalDataManager.getMaskedBankAccounts();
+        if (mBankAccounts.length == 0) {
+            return;
+        }
+
+        boolean isFacilitatedPaymentsPixEnabled =
+                mPersonalDataManager.getFacilitatedPaymentsPixPref();
+        ChromeSwitchPreference pixSwitch = new ChromeSwitchPreference(getStyledContext());
+        pixSwitch.setChecked(isFacilitatedPaymentsPixEnabled);
+        pixSwitch.setKey(PREFERENCE_KEY_PIX);
+        pixSwitch.setTitle(R.string.settings_manage_other_financial_accounts_pix);
+        getPreferenceScreen().addPreference(pixSwitch);
+        if (isFacilitatedPaymentsPixEnabled) {
+            // Show bank accounts only if the Pix switch is enabled.
+            addPixAccountPreferences();
+        }
+        pixSwitch.setOnPreferenceChangeListener(this);
+    }
+
+    private void removePixAccountPreferences() {
+        for (BankAccount bankAccount : mBankAccounts) {
+            Preference bankAccountPref =
+                    getPreferenceScreen()
+                            .findPreference(
+                                    String.format(
+                                            PREFERENCE_KEY_PIX_BANK_ACCOUNT,
+                                            bankAccount.getInstrumentId()));
+            if (bankAccountPref != null) {
+                getPreferenceScreen().removePreference(bankAccountPref);
             }
         }
     }
 
+    private void addPixAccountPreferences() {
+        for (BankAccount bankAccount : mBankAccounts) {
+            getPreferenceScreen().addPreference(getPreferenceForBankAccount(bankAccount));
+        }
+    }
+
     private Preference getPreferenceForBankAccount(BankAccount bankAccount) {
         Preference bankAccountPref = new Preference(getStyledContext());
 
@@ -149,6 +177,22 @@
         return getPreferenceManager().getContext();
     }
 
+    // Preference.OnPreferenceChangeListener override.
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference.getKey().equals(PREFERENCE_KEY_PIX)) {
+            boolean isPixEnabled = (boolean) newValue;
+            mPersonalDataManager.setFacilitatedPaymentsPixPref(isPixEnabled);
+            if (isPixEnabled) {
+                addPixAccountPreferences();
+            } else {
+                removePixAccountPreferences();
+            }
+            return true;
+        }
+        return false;
+    }
+
     // PersonalDataManagerObserver implementation.
     @Override
     public void onPersonalDataChanged() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
index cf8d36cd..7b285bd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
@@ -166,13 +166,14 @@
         TabGroupModelFilter filter =
                 (TabGroupModelFilter)
                         mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(incognito);
+        if (!filter.tabGroupExistsForRootId(groupRootId)) return;
+
         String titleString = filter.getTabGroupTitle(groupRootId);
         getUpdatedGroupTitle(groupRootId, titleString, incognito);
     }
 
     public String getUpdatedGroupTitle(int groupRootId, String titleString, boolean incognito) {
-        // TODO(crbug.com/331642736): Investigate skipping creating the bitmap for empty titles.
-        if (titleString == null) titleString = "";
+        if (TextUtils.isEmpty(titleString)) return null;
 
         getUpdatedGroupTitleInternal(groupRootId, titleString, incognito);
         return titleString;
@@ -305,8 +306,6 @@
     }
 
     public void removeGroupTitle(int rootId) {
-        // TODO(crbug.com/326492787): Currently unused. Call to release bitmaps when we actually
-        // observe tab group changes (i.e. call this when a tab group is destroyed).
         Title title = mGroupTitles.get(rootId);
         if (title == null) return;
         title.unregister();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java
index e1a76e7..cc22c91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java
@@ -50,9 +50,17 @@
             StripLayoutView[] indexOrderedViews, float xOffset, float visibleWidth) {
         for (int i = 0; i < indexOrderedViews.length; i++) {
             StripLayoutView view = indexOrderedViews[i];
-            view.setVisible(
-                    (view.getDrawX() + view.getWidth()) >= xOffset
-                            && view.getDrawX() <= xOffset + visibleWidth);
+            float drawX;
+            float width;
+            if (view instanceof StripLayoutGroupTitle groupTitle) {
+                drawX = groupTitle.getPaddedX();
+                width = groupTitle.getBottomIndicatorWidth();
+            } else {
+                drawX = view.getDrawX();
+                width = view.getWidth();
+            }
+
+            view.setVisible((drawX + width) >= xOffset && drawX <= xOffset + visibleWidth);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
index d269327..33c2d05c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
@@ -20,6 +20,18 @@
  * onto the GL canvas.
  */
 public class StripLayoutGroupTitle extends StripLayoutView {
+    /** Delegate for additional group title functionality. */
+    public interface StripLayoutGroupTitleDelegate {
+        // TODO(https://crbug.com/326492955): Implement click to collapse/expand.
+
+        /**
+         * Releases the resources associated with this group indicator.
+         *
+         * @param rootId The root ID of the given group indicator.
+         */
+        void releaseResourcesForGroupTitle(int rootId);
+    }
+
     /** A property for animations to use for changing the width of the bottom indicator. */
     public static final FloatProperty<StripLayoutGroupTitle> BOTTOM_INDICATOR_WIDTH =
             new FloatProperty<>("bottomIndicatorWidth") {
@@ -51,6 +63,9 @@
     private static final int EFFECTIVE_MIN_WIDTH = MIN_VISUAL_WIDTH_DP + WIDTH_MARGINS_DP;
     private static final int EFFECTIVE_MAX_WIDTH = MAX_VISUAL_WIDTH_DP + WIDTH_MARGINS_DP;
 
+    // External influences.
+    private final StripLayoutGroupTitleDelegate mDelegate;
+
     // State variables.
     private final boolean mIncognito;
 
@@ -74,6 +89,7 @@
     /**
      * Create a {@link StripLayoutGroupTitle} that represents the TabGroup for the {@code rootId}.
      *
+     * @param delegate The delegate for additional strip group title functionality.
      * @param incognito Whether or not this tab group is Incognito.
      * @param rootId The root ID for the tab group.
      * @param title The title of the tab group, if it is set. Null otherwise.
@@ -81,6 +97,7 @@
      * @param color The color of the tab group.
      */
     public StripLayoutGroupTitle(
+            StripLayoutGroupTitleDelegate delegate,
             boolean incognito,
             int rootId,
             @Nullable String title,
@@ -88,6 +105,7 @@
             @ColorInt int color) {
         assert rootId != Tab.INVALID_TAB_ID : "Tried to create a group title for an invalid group.";
 
+        mDelegate = delegate;
         mIncognito = incognito;
 
         updateRootId(rootId);
@@ -142,6 +160,13 @@
     }
 
     @Override
+    public void setVisible(boolean visible) {
+        super.setVisible(visible);
+
+        if (!visible) mDelegate.releaseResourcesForGroupTitle(mRootId);
+    }
+
+    @Override
     public String getAccessibilityDescription() {
         return mAccessibilityDescription;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index ac8d6ac3..05ef800 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -51,6 +51,8 @@
 import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.CompositorOnClickHandler;
 import org.chromium.chrome.browser.compositor.layouts.components.TintedCompositorButton;
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackScroller;
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutGroupTitle.StripLayoutGroupTitleDelegate;
+import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutTab.StripLayoutTabDelegate;
 import org.chromium.chrome.browser.compositor.overlays.strip.TabLoadTracker.TabLoadTrackerCallback;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.animation.CompositorAnimator;
@@ -59,6 +61,7 @@
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.chrome.browser.tasks.tab_groups.TabGroupColorUtils;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilterObserver;
 import org.chromium.chrome.browser.tasks.tab_management.ColorPickerUtils;
@@ -85,7 +88,7 @@
  *
  * <p>The stacking and visual behavior is driven by setting a {@link StripStacker}.
  */
-public class StripLayoutHelper implements StripLayoutTab.StripLayoutTabDelegate {
+public class StripLayoutHelper implements StripLayoutTabDelegate, StripLayoutGroupTitleDelegate {
     /** A property for animations to use for changing the X offset of the tab. */
     public static final FloatProperty<StripLayoutHelper> SCROLL_OFFSET =
             new FloatProperty<StripLayoutHelper>("scrollOffset") {
@@ -173,6 +176,7 @@
     private static final float CLOSE_BTN_VISIBILITY_THRESHOLD_START = 96.f;
     private static final long TAB_SWITCH_METRICS_MAX_ALLOWED_SCROLL_INTERVAL =
             DateUtils.MINUTE_IN_MILLIS;
+    private static final int INVALID_COLOR_ID = -1;
 
     // Histogram Constants
     private static final String PLACEHOLDER_LEFTOVER_TABS_HISTOGRAM_NAME =
@@ -252,14 +256,14 @@
                     if (groupTitle == null) return;
 
                     int widthPx = mLayerTitleCache.getGroupTitleWidth(mIncognito, newTitle);
-                    updateGroupTitle(rootId, newTitle, widthPx);
+                    updateGroupTitle(groupTitle, newTitle, widthPx);
                     updateGroupAccessibilityDescription(groupTitle);
 
-                    // TODO(crbug.com/331664842): When group title bitmaps are culled, guard this
-                    //  with groupTitle.isVisible() check to avoid creating when unneeded.
-                    mLayerTitleCache.getUpdatedGroupTitle(
-                            rootId, groupTitle.getTitle(), mIncognito);
-                    mRenderHost.requestRender();
+                    if (groupTitle.isVisible()) {
+                        mLayerTitleCache.getUpdatedGroupTitle(
+                                rootId, groupTitle.getTitle(), mIncognito);
+                        mRenderHost.requestRender();
+                    }
                 }
 
                 @Override
@@ -274,18 +278,25 @@
                     groupTitle.updateTint(color);
 
                     // Title may also need to change color.
-                    // TODO(crbug.com/331664842): When group title bitmaps are culled, guard this
-                    //  with groupTitle.isVisible() check to avoid creating when unneeded.
-                    mLayerTitleCache.getUpdatedGroupTitle(
-                            rootId, groupTitle.getTitle(), mIncognito);
-                    mRenderHost.requestRender();
+                    if (groupTitle.isVisible()) {
+                        mLayerTitleCache.getUpdatedGroupTitle(
+                                rootId, groupTitle.getTitle(), mIncognito);
+                        mRenderHost.requestRender();
+                    }
                 }
 
                 @Override
                 public void didChangeGroupRootId(int oldRootId, int newRootId) {
+                    releaseResourcesForGroupTitle(oldRootId);
+
                     StripLayoutGroupTitle groupTitle = findGroupTitle(oldRootId);
                     if (groupTitle != null) groupTitle.updateRootId(newRootId);
                 }
+
+                @Override
+                public void didRemoveTabGroup(int oldRootId) {
+                    releaseResourcesForGroupTitle(oldRootId);
+                }
             };
 
     // External influences
@@ -2422,11 +2433,9 @@
         groupTitle.setAccessibilityDescription(buildGroupAccessibilityDescription(groupTitle));
     }
 
-    private void updateGroupTitle(int rootId, String title, int widthPx) {
-        StripLayoutGroupTitle groupTitle = findGroupTitle(rootId);
-        if (groupTitle == null) return;
-
-        updateGroupTitle(groupTitle, title, widthPx);
+    @Override
+    public void releaseResourcesForGroupTitle(int rootId) {
+        mLayerTitleCache.removeGroupTitle(rootId);
     }
 
     private void updateGroupTitle(StripLayoutGroupTitle groupTitle, String title, int widthPx) {
@@ -2455,7 +2464,12 @@
     }
 
     private StripLayoutGroupTitle createGroupTitle(int rootId) {
-        @TabGroupColorId int colorId = mTabGroupModelFilter.getTabGroupColor(rootId);
+        int colorId = TabGroupColorUtils.getTabGroupColor(rootId);
+        // If the color is invalid, temporarily assign a default placeholder color.
+        if (colorId == INVALID_COLOR_ID) {
+            colorId = TabGroupColorId.GREY;
+        }
+
         @ColorInt
         int color = ColorPickerUtils.getTabGroupColorPickerItemColor(mContext, colorId, mIncognito);
         String titleString = mTabGroupModelFilter.getTabGroupTitle(rootId);
@@ -2468,7 +2482,7 @@
         }
 
         StripLayoutGroupTitle groupTitle =
-                new StripLayoutGroupTitle(mIncognito, rootId, titleString, textWidth, color);
+                new StripLayoutGroupTitle(this, mIncognito, rootId, titleString, textWidth, color);
         updateGroupAccessibilityDescription(groupTitle);
         pushPropertiesToGroupTitle(groupTitle);
 
@@ -2862,7 +2876,7 @@
 
         // 4. Calculate which tabs are visible.
         float stripWidth = getVisibleRightBound() - getVisibleLeftBound();
-        mStripStacker.performOcclusionPass(mStripTabs, getVisibleLeftBound(), stripWidth);
+        mStripStacker.performOcclusionPass(mStripViews, getVisibleLeftBound(), stripWidth);
 
         // 5. Create render list.
         createRenderList();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index 82e5217..8c453e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -947,8 +947,7 @@
      *     or not the newly created {@link WebContents} will be hidden or not.
      * @param tabState State containing information about this Tab, if it was persisted.
      * @param initializeRenderer Determines whether or not we initialize renderer with {@link
-     *     WebContents} creation. The CREATE_NEW_TAB_INITIALIZE_RENDERER feature also controls this
-     *     parameter, which initializes the renderer when it is enabled.
+     *     WebContents} creation.
      */
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     void initialize(
@@ -962,14 +961,7 @@
             boolean initializeRenderer) {
         try {
             TraceEvent.begin("Tab.initialize");
-            // If the feature is enabled, the renderer will always be initialized during the
-            // WebContents creation. It is an experimental performance optimization to speed
-            // up navigation.
-            initializeRenderer =
-                    ChromeFeatureList.isEnabled(
-                                    ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
-                            ? true
-                            : initializeRenderer;
+
             if (parent != null) {
                 mParentId = parent.getId();
             }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ViewTransitionPixelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ViewTransitionPixelTest.java
index d47b37a90..def1c333 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ViewTransitionPixelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ViewTransitionPixelTest.java
@@ -26,9 +26,7 @@
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.CriteriaNotSatisfiedException;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.fullscreen.FullscreenManagerTestUtils;
 import org.chromium.chrome.browser.tab.TabStateBrowserControlsVisibilityDelegate;
@@ -428,8 +426,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testVirtualKeyboardResizesVisual() throws Throwable {
         startKeyboardTest(VirtualKeyboardMode.RESIZES_VISUAL);
 
@@ -472,8 +468,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testVirtualKeyboardResizesContent() throws Throwable {
         startKeyboardTest(VirtualKeyboardMode.RESIZES_CONTENT);
 
@@ -516,8 +510,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testDialog() throws Throwable {
         String url = "/chrome/test/data/android/view_transition_dialog.html";
         mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(url));
@@ -548,8 +540,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testPageWiderThanICB() throws Throwable {
         String url = "/chrome/test/data/android/view_transition_wider_than_icb.html";
         mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(url));
@@ -585,8 +575,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testBrowserControlsRootSnapshotControlsOverlay() throws Throwable {
         String url = "/chrome/test/data/android/view_transition_browser_controls.html";
         mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(url));
@@ -634,8 +622,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testBrowserControlsRootSnapshotControlsPush() throws Throwable {
         String url = "/chrome/test/data/android/view_transition_browser_controls.html";
         mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(url));
@@ -675,8 +661,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testBrowserControlsChildSnapshotControlsOverlay() throws Throwable {
         String url = "/chrome/test/data/android/view_transition_browser_controls_child.html";
         mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(url));
@@ -723,8 +707,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testBrowserControlsChildSnapshotControlsPush() throws Throwable {
         String url = "/chrome/test/data/android/view_transition_browser_controls_child.html";
         mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(url));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
index 0453e4c..e32ce9b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
@@ -33,11 +33,9 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.WarmupManager.SpareTabFinalStatus;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.profiles.OTRProfileID;
@@ -550,31 +548,6 @@
         pageLoadHistogramWatcher.pollInstrumentationThreadUntilSatisfied();
     }
 
-    /**
-     * Tests that we are able to create new tab along with initializing renderer when the feature
-     * CREATE_NEW_TAB_INITIALIZE_RENDERER is enabled.
-     */
-    @Test
-    @MediumTest
-    @Feature({"SpareTab"})
-    @UiThreadTest
-    @EnableFeatures({ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER})
-    public void testOnTabCreationWithInitializeRenderer() {
-        Profile profile = getProfile(ProfileType.REGULAR_PROFILE);
-        mWarmupManager.createRegularSpareTab(profile);
-        Assert.assertTrue(mWarmupManager.hasSpareTab(profile));
-
-        Tab tab = mWarmupManager.takeSpareTab(profile, TabLaunchType.FROM_TAB_GROUP_UI);
-        WebContents webContents = tab.getWebContents();
-        Assert.assertNotNull(tab);
-        Assert.assertNotNull(webContents);
-        Assert.assertEquals(TabLaunchType.FROM_TAB_GROUP_UI, tab.getLaunchType());
-
-        // RenderFrame should be created when the CREATE_NEW_TAB_INITIALIZE_RENDERER is enabled.
-        Assert.assertTrue(webContents.getMainFrame().isRenderFrameLive());
-        tab.destroy();
-    }
-
     @Test
     @SmallTest
     @Restriction({DeviceRestriction.RESTRICTION_TYPE_NON_AUTO})
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 6eaaa1d..e1dec76 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
@@ -30,6 +30,8 @@
 import org.chromium.chrome.browser.autofill.AutofillUiUtils.CardIconSpecs;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -40,6 +42,8 @@
 import org.chromium.components.autofill.payments.PaymentRail;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.image_fetcher.test.TestImageFetcher;
+import org.chromium.components.prefs.PrefService;
+import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.url.GURL;
 
@@ -47,8 +51,8 @@
 
 /** Instrumentation tests for FinancialAccountsManagementFragment. */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@Batch(Batch.PER_CLASS)
 @EnableFeatures({ChromeFeatureList.AUTOFILL_ENABLE_SYNCING_OF_PIX_BANK_ACCOUNTS})
+@Batch(Batch.PER_CLASS)
 public class FinancialAccountsManagementFragmentTest {
     @Rule public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
     @Rule public final AutofillTestRule rule = new AutofillTestRule();
@@ -98,6 +102,8 @@
                                     CardIconSpecs.create(
                                             ContextUtils.getApplicationContext(),
                                             CardIconSize.LARGE));
+                    // Set the Pix pref to true.
+                    getPrefService().setBoolean(Pref.FACILITATED_PAYMENTS_PIX, true);
                 });
     }
 
@@ -112,44 +118,68 @@
                 });
     }
 
+    // Test that when Pix accounts are available the Pix preference toggle is shown.
     @Test
     @MediumTest
-    public void testPixAccountAvailable_PixPrefShown() throws Exception {
+    public void testPixAccountAvailable_pixSwitchShown() throws Exception {
         AutofillTestHelper.addMaskedBankAccount(PIX_BANK_ACCOUNT);
 
         SettingsActivity activity = mSettingsActivityTestRule.startSettingsActivity();
 
         // Verify that the switch preference for Pix is displayed.
-        ChromeSwitchPreference pixSwitch =
-                (ChromeSwitchPreference)
-                        getPreferenceScreen(activity)
-                                .findPreference(
-                                        FinancialAccountsManagementFragment.PREFERENCE_KEY_PIX);
+        ChromeSwitchPreference pixSwitch = getPixSwitchPreference(activity);
         assertThat(pixSwitch).isNotNull();
     }
 
+    // Test that when Pix accounts are not available the Pix preference toggle is not shown.
     @Test
     @MediumTest
-    public void testPixAccountNotAvailable_PixPrefNotShown() throws Exception {
+    public void testPixAccountNotAvailable_pixSwitchNotShown() throws Exception {
         SettingsActivity activity = mSettingsActivityTestRule.startSettingsActivity();
 
         // Verify that the switch preference for Pix is not displayed.
-        ChromeSwitchPreference pixSwitch =
-                (ChromeSwitchPreference)
-                        getPreferenceScreen(activity)
-                                .findPreference(
-                                        FinancialAccountsManagementFragment.PREFERENCE_KEY_PIX);
+        ChromeSwitchPreference pixSwitch = getPixSwitchPreference(activity);
         assertThat(pixSwitch).isNull();
     }
 
+    // Test that when Pix profile preference is set to true, the Pix toggle is checked.
+    @Test
+    @MediumTest
+    public void testPixPrefEnabled_pixSwitchEnabled() throws Exception {
+        AutofillTestHelper.addMaskedBankAccount(PIX_BANK_ACCOUNT);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    getPrefService().setBoolean(Pref.FACILITATED_PAYMENTS_PIX, true);
+                });
+
+        SettingsActivity activity = mSettingsActivityTestRule.startSettingsActivity();
+
+        // Verify that the switch preference for Pix is displayed and is checked.
+        ChromeSwitchPreference pixSwitch = getPixSwitchPreference(activity);
+        assertThat(pixSwitch.isChecked()).isTrue();
+    }
+
+    // Test that when the Pix profile preference is set to false, the Pix toggle is not checked.
+    @Test
+    @MediumTest
+    public void testPixPrefDisabled_pixSwitchDisabled() throws Exception {
+        AutofillTestHelper.addMaskedBankAccount(PIX_BANK_ACCOUNT);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    getPrefService().setBoolean(Pref.FACILITATED_PAYMENTS_PIX, false);
+                });
+
+        SettingsActivity activity = mSettingsActivityTestRule.startSettingsActivity();
+
+        // Verify that the switch preference for Pix is displayed the is not checked.
+        ChromeSwitchPreference pixSwitch = getPixSwitchPreference(activity);
+        assertThat(pixSwitch.isChecked()).isFalse();
+    }
+
     @Test
     @MediumTest
     public void testPixAccountShown() {
         AutofillTestHelper.addMaskedBankAccount(PIX_BANK_ACCOUNT);
-        String bankAccountPrefKey =
-                String.format(
-                        FinancialAccountsManagementFragment.PREFERENCE_KEY_PIX_BANK_ACCOUNT,
-                        PIX_BANK_ACCOUNT.getInstrumentId());
 
         SettingsActivity activity = mSettingsActivityTestRule.startSettingsActivity();
 
@@ -158,8 +188,7 @@
                         "Pix  •  %s •• %s",
                         activity.getString(R.string.bank_account_type_checking),
                         PIX_BANK_ACCOUNT.getAccountNumberSuffix());
-        Preference bankAccountPref =
-                getPreferenceScreen(activity).findPreference(bankAccountPrefKey);
+        Preference bankAccountPref = getBankAccountPreference(activity, PIX_BANK_ACCOUNT);
         assertThat(bankAccountPref.getTitle()).isEqualTo(PIX_BANK_ACCOUNT.getBankName());
         assertThat(bankAccountPref.getSummary()).isEqualTo(expectedPrefSummary);
         assertThat(bankAccountPref.getWidgetLayoutResource())
@@ -246,8 +275,74 @@
         assertThat(bankAccountPref.getIcon()).isNull();
     }
 
+    // Test that Pix bank accounts are removed when the Pix toggle is turned off.
+    @Test
+    @MediumTest
+    public void testPixSwitchDisabled_bankAccountPrefsRemoved() {
+        AutofillTestHelper.addMaskedBankAccount(PIX_BANK_ACCOUNT);
+        SettingsActivity activity = mSettingsActivityTestRule.startSettingsActivity(new Bundle());
+        ChromeSwitchPreference pixSwitch = getPixSwitchPreference(activity);
+        Preference bankAccountPref = getBankAccountPreference(activity, PIX_BANK_ACCOUNT);
+        assertThat(bankAccountPref).isNotNull();
+
+        // Set the Pix toggle to off.
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    pixSwitch.performClick();
+                });
+
+        // Verify that the bank account preference is now null.
+        bankAccountPref = getBankAccountPreference(activity, PIX_BANK_ACCOUNT);
+        assertThat(bankAccountPref).isNull();
+    }
+
+    // Test that Pix bank accounts are added when the Pix toggle is turned on.
+    @Test
+    @MediumTest
+    public void testPixSwitchEnabled_bankAccountPrefsAdded() {
+        AutofillTestHelper.addMaskedBankAccount(PIX_BANK_ACCOUNT);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    getPrefService().setBoolean(Pref.FACILITATED_PAYMENTS_PIX, false);
+                });
+        SettingsActivity activity = mSettingsActivityTestRule.startSettingsActivity(new Bundle());
+        ChromeSwitchPreference pixSwitch = getPixSwitchPreference(activity);
+
+        Preference bankAccountPref = getBankAccountPreference(activity, PIX_BANK_ACCOUNT);
+        assertThat(bankAccountPref).isNull();
+
+        // Set the Pix toggle to on.
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    pixSwitch.performClick();
+                });
+
+        // Verify that the bank account preference is now not null.
+        bankAccountPref = getBankAccountPreference(activity, PIX_BANK_ACCOUNT);
+        assertThat(bankAccountPref).isNotNull();
+    }
+
     private static PreferenceScreen getPreferenceScreen(SettingsActivity activity) {
         return ((FinancialAccountsManagementFragment) activity.getMainFragment())
                 .getPreferenceScreen();
     }
+
+    private static PrefService getPrefService() {
+        return UserPrefs.get(ProfileManager.getLastUsedRegularProfile());
+    }
+
+    private static ChromeSwitchPreference getPixSwitchPreference(SettingsActivity activity) {
+        return (ChromeSwitchPreference)
+                getPreferenceScreen(activity)
+                        .findPreference(FinancialAccountsManagementFragment.PREFERENCE_KEY_PIX);
+    }
+
+    private static Preference getBankAccountPreference(
+            SettingsActivity activity, BankAccount bankAccount) {
+        String bankAccountPrefKey =
+                String.format(
+                        FinancialAccountsManagementFragment.PREFERENCE_KEY_PIX_BANK_ACCOUNT,
+                        bankAccount.getInstrumentId());
+        return getPreferenceScreen(activity).findPreference(bankAccountPrefKey);
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/dragdrop/DragAndDropLauncherActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/dragdrop/DragAndDropLauncherActivityTest.java
index 05e0d5d..7bf9f9f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/dragdrop/DragAndDropLauncherActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/dragdrop/DragAndDropLauncherActivityTest.java
@@ -206,10 +206,7 @@
      */
     @Test
     @LargeTest
-    @EnableFeatures({
-        ChromeFeatureList.DRAG_DROP_TAB_TEARING,
-        ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID
-    })
+    @EnableFeatures(ChromeFeatureList.DRAG_DROP_TAB_TEARING)
     public void testDraggedTab_newWindow() throws Exception {
         HistogramWatcher histogramExpectation =
                 HistogramWatcher.newSingleRecordWatcher(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
index ef9b5c4..c58fa10 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
@@ -636,7 +636,6 @@
 
     @Test
     @Config(sdk = VERSION_CODES.R)
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     public void testDragDropInstances_Success() {
         enableMultiInstance();
         initializeTest();
@@ -647,7 +646,6 @@
 
     @Test
     @Config(sdk = VERSION_CODES.Q)
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     public void testDragDropInstances_MultiInstanceNotEnabled_ReturnsNull() {
         initializeTest();
         assertNull(
@@ -670,7 +668,6 @@
 
     @Test
     @Config(sdk = VERSION_CODES.S)
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     public void testGetDragListener() {
         enableMultiInstance();
         initializeTest();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index 61b2e2e6..2ee7a878 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -1537,7 +1537,6 @@
 
     @Test
     @Config(sdk = Build.VERSION_CODES.R)
-    @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     public void testOnLongPress_WithDragDrop_OnTab() {
         // Extra setup for DragDrop
         setTabDragSourceMock();
@@ -1597,7 +1596,6 @@
 
     @Test
     @Config(sdk = Build.VERSION_CODES.R)
-    @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     public void testOnLongPress_WithDragDrop_OffTab() {
         // Extra setup for DragDrop
         setTabDragSourceMock();
@@ -3411,7 +3409,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     @Config(sdk = Build.VERSION_CODES.R)
     public void testDrag_AllowMovingTabOutOfStripLayout_SetActiveTab() {
         // Setup with 10 tabs and select tab 5.
@@ -3448,7 +3445,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     @Config(sdk = Build.VERSION_CODES.R)
     public void testDrag_clearState() {
         // Initialize with 10 tabs.
@@ -3468,7 +3464,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     @Config(sdk = Build.VERSION_CODES.R)
     public void testDrag_sendMoveWindowBroadcast_success() {
         // Setup with tabs and select first tab.
@@ -3482,7 +3477,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     @Config(sdk = Build.VERSION_CODES.R)
     public void testDrag_DragActiveClickedTabOntoStrip() {
         // Setup and mark the active clicked tab.
@@ -3507,7 +3501,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     @Config(sdk = Build.VERSION_CODES.R)
     public void testDrag_DragActiveClickedTabOutOfStrip() {
         // Setup and mark the active clicked tab.
@@ -3544,7 +3537,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     @Config(sdk = Build.VERSION_CODES.R)
     public void testDrag2_DragActiveClickedTabOutOfStrip() {
         // Setup and mark the active clicked tab.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripTabDragShadowViewUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripTabDragShadowViewUnitTest.java
index 3f6ef17..c3c0a99 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripTabDragShadowViewUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripTabDragShadowViewUnitTest.java
@@ -41,10 +41,8 @@
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabUtils;
 import org.chromium.chrome.browser.tab_ui.TabContentManager;
@@ -56,7 +54,6 @@
 
 /** Unit tests for {@link StripTabDragShadowView}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
 public class StripTabDragShadowViewUnitTest {
     @Rule
     public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSourceTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSourceTest.java
index 27f79a0d..914b056 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSourceTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSourceTest.java
@@ -91,7 +91,6 @@
 import java.util.Set;
 
 /** Tests for {@link TabDragSource}. */
-@EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
 @DisableFeatures(ChromeFeatureList.DRAG_DROP_TAB_TEARING)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(qualifiers = "sw600dp", sdk = VERSION_CODES.S, shadows = ShadowToast.class)
@@ -239,7 +238,6 @@
     }
 
     @DisableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     @Test
     public void test_startTabDragAction_withTabLinkDragDropFF_returnsTrueForValidTab() {
         // Act and verify.
@@ -334,7 +332,6 @@
         ChromeFeatureList.TAB_DRAG_DROP_ANDROID,
         ChromeFeatureList.DRAG_DROP_TAB_TEARING
     })
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     public void test_startTabDragAction_returnFalseForNonSplitScreen() {
         // Set params.
         when(mMultiWindowUtils.isInMultiWindowMode(mActivity)).thenReturn(false);
@@ -352,7 +349,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     @DisableFeatures({
         ChromeFeatureList.TAB_DRAG_DROP_ANDROID,
         ChromeFeatureList.DRAG_DROP_TAB_TEARING
@@ -459,7 +455,6 @@
     }
 
     @Test
-    @EnableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     @DisableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
     public void test_onProvideShadowMetrics_withTabLinkDragDropFF() {
         // Call startDrag to set class variables.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
index 1c38f4f..014aa669 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
@@ -27,7 +27,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
@@ -44,12 +43,7 @@
 import java.lang.ref.WeakReference;
 
 @RunWith(org.chromium.base.test.BaseRobolectricTestRunner.class)
-@EnableFeatures({
-    ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID,
-})
-@DisableFeatures({
-    ChromeFeatureList.TAB_DRAG_DROP_ANDROID,
-})
+@DisableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
 public class ChromeTabbedOnDragListenerUnitTest {
     private static final int SOURCE_INSTANCE_ID = 1;
     @Rule public MockitoRule mMockitoProcessorRule = MockitoJUnit.rule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
index a835481..d882f85ed 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
@@ -1029,6 +1029,7 @@
     @SmallTest
     @Config(sdk = 31)
     @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
+    @DisableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     public void testTabMove_MoveTabToNewWindow_calledWithDesiredParameters() {
         mMultiInstanceManager.mTestBuildInstancesList = true;
         MultiWindowTestUtils.enableMultiInstance();
@@ -1079,6 +1080,7 @@
     @SmallTest
     @Config(sdk = 31)
     @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
+    @DisableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     public void testTabMove_MoveTabToNewWindow_BeyondMaxWindows_CallsOnly_OpenNewWindow() {
         mMultiInstanceManager.mTestBuildInstancesList = true;
         MultiWindowTestUtils.enableMultiInstance();
@@ -1107,6 +1109,7 @@
 
     @Test
     @EnableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
+    @DisableFeatures(ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID)
     @Config(sdk = 31)
     public void testTabMove_MoveTabToCurrentWindow_calledWithDesiredParameters() {
         int tabAtIndex = 0;
@@ -1130,7 +1133,10 @@
     }
 
     @Test
-    @DisableFeatures(ChromeFeatureList.TAB_DRAG_DROP_ANDROID)
+    @DisableFeatures({
+        ChromeFeatureList.TAB_DRAG_DROP_ANDROID,
+        ChromeFeatureList.TAB_LINK_DRAG_DROP_ANDROID
+    })
     public void testTabMove_MoveTabToWindow_notCalled() {
         int tabAtIndex = 0;
         MultiInstanceManagerApi31 multiInstanceManager =
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index bc92b9e..563b8ca 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -220,6 +220,7 @@
 #define IDC_SHOW_HISTORY_CLUSTERS_SIDE_PANEL 40025
 #define IDC_PROFILING_ENABLED           40028
 #define IDC_BOOKMARKS_MENU              40029
+#define IDC_SAVED_TAB_GROUPS_MENU       40030
 #define IDC_EXTENSION_ERRORS            40031
 #define IDC_SHOW_SETTINGS_CHANGE_FIRST  40033
 #define IDC_SHOW_SETTINGS_CHANGE_LAST   40133
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 797057c..fb0a6ee 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -11131,6 +11131,9 @@
         <message name="IDS_CREATE_NEW_TAB_GROUP" desc="The label of item in Everything menu to create a new tab group.">
           Create new tab group
         </message>
+        <message name="IDS_SAVED_TAB_GROUPS_MENU" desc="The title of the 'Tab groups' menu in the App Menu.">
+      Tab groups
+    </message>
       </if>
       <if expr="use_titlecase">
         <message name="IDS_SAVED_TAB_GROUP_TABS_COUNT" desc="In Title Case: Title of unnamed saved tab group in Everything menu. [ICU_Syntax]">
@@ -11139,6 +11142,9 @@
         <message name="IDS_CREATE_NEW_TAB_GROUP" desc="The label of item in Everything menu to create a new tab group.">
           Create New Tab Group
         </message>
+        <message name="IDS_SAVED_TAB_GROUPS_MENU" desc="The title of the 'Tab Groups' menu in the App Menu.">
+      Tab Groups
+    </message>
       </if>
 
       <!-- Tab Group Editor Bubble -->
diff --git a/chrome/app/generated_resources_grd/IDS_SAVED_TAB_GROUPS_MENU.png.sha1 b/chrome/app/generated_resources_grd/IDS_SAVED_TAB_GROUPS_MENU.png.sha1
new file mode 100644
index 0000000..38f388a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SAVED_TAB_GROUPS_MENU.png.sha1
@@ -0,0 +1 @@
+3f7a9988909bb052a233b23337920f020c9d916d
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 9584f36..73cba193 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -6302,6 +6302,9 @@
   <message name="IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_SUBLABEL" desc="The sublabel that describes the feature where users can set up on-device parental controls to block or enable apps.">
     Manage access and block apps on this Chromebook.
   </message>
+  <message name="IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_SET_UP_BUTTON" desc="Text for the button that allows users to set up a PIN for on-device parental controls.">
+    Set up
+  </message>
 
    <!-- Apps > Manage your apps > Borealis -->
   <message name="IDS_SETTINGS_APPS_BOREALIS_MAIN_PERMISSION_TEXT" desc="Description in Steam (name of app, manages other apps) settings page.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_SET_UP_BUTTON.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_SET_UP_BUTTON.png.sha1
new file mode 100644
index 0000000..a6d9a5d9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_SET_UP_BUTTON.png.sha1
@@ -0,0 +1 @@
+c3055e7ad3a46193c4e01445c09e822959b4e7ac
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 8aaf127f..d20fc2a 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -435,6 +435,7 @@
   "+media/renderers",
   "+media/webrtc",  # For webrtc media switches.
   "+mojo/core/embedder",
+  "+pdf/pdf_features.h",
   "+ppapi/c",  # For various types.
   "+ppapi/host",
   "+ppapi/proxy",
@@ -595,9 +596,6 @@
     # For GetVirtualKeyboardController.
     "+ash/keyboard/ui/keyboard_ui_controller.h",
   ],
-  "chrome_back_forward_cache_browsertest.cc": [
-    "+pdf/pdf_features.h",
-  ],
   "chrome_browsing_data_remover_delegate_unittest.cc": [
     "+services/network/network_context.h",
     "+services/network/network_service.h",
@@ -614,12 +612,8 @@
   "chrome_navigation_browsertest.cc" : [
     "+content/common/content_navigation_policy.h",
   ],
-  "chrome_web_platform_security_metrics_browsertest.cc" : [
-    "+pdf/pdf_features.h",
-  ],
   "about_flags\.cc" : [
     "+mojo/core/embedder/features.h",
-    "+pdf/pdf_features.h",
     "+sandbox/policy/features.h",
   ],
   "dbus_memory_pressure_evaluator_linux(_unittest)?\.(cc|h)" : [
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b999ead1..fe0c8e2 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1302,28 +1302,6 @@
 };
 
 const FeatureEntry::FeatureParam
-    kJourneysLabelsWithSearchVisitEntitiesParams[] = {
-        {"labels_from_search_visit_entities", "true"},
-};
-const FeatureEntry::FeatureParam kJourneysLabelsWithEntitiesParams[] = {
-    {"labels_from_entities", "true"},
-};
-const FeatureEntry::FeatureParam
-    kJourneysLabelsWithEntitiesNoHostnamesParams[] = {
-        {"labels_from_hostnames", "false"},
-        {"labels_from_entities", "true"},
-};
-const FeatureEntry::FeatureVariation kJourneysLabelsVariations[] = {
-    {"With Entities", kJourneysLabelsWithEntitiesParams,
-     std::size(kJourneysLabelsWithEntitiesParams), nullptr},
-    {"With Entities, No Hostnames",
-     kJourneysLabelsWithEntitiesNoHostnamesParams,
-     std::size(kJourneysLabelsWithEntitiesNoHostnamesParams), nullptr},
-    {"With Search Entities", kJourneysLabelsWithSearchVisitEntitiesParams,
-     std::size(kJourneysLabelsWithSearchVisitEntitiesParams), nullptr},
-};
-
-const FeatureEntry::FeatureParam
     kOmniboxCompanyEntityIconAdjustmentLeastAggressive[] = {
         {"OmniboxCompanyEntityAdjustmentGroup", "least-aggressive"}};
 const FeatureEntry::FeatureParam kOmniboxCompanyEntityIconAdjustmentModerate[] =
@@ -6351,12 +6329,6 @@
                                     kJourneysVariations,
                                     "HistoryJourneys")},
 
-    {"history-journeys-labels", flag_descriptions::kJourneysLabelsName,
-     flag_descriptions::kJourneysLabelsDescription, kOsDesktop | kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(history_clusters::internal::kJourneysLabels,
-                                    kJourneysLabelsVariations,
-                                    "HistoryJourneysLabels")},
-
     {"history-journeys-show-all-clusters",
      flag_descriptions::kJourneysShowAllClustersName,
      flag_descriptions::kJourneysShowAllClustersDescription,
@@ -10355,12 +10327,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_ANDROID)
-    {"ml-mobile-pwa-prompt", flag_descriptions::kMobilePWAInstallPromptMlName,
-     flag_descriptions::kMobilePWAInstallPromptMlDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(webapps::features::kInstallPromptSegmentation)},
-#endif
-
-#if BUILDFLAG(IS_ANDROID)
     {"mouse-and-trackpad-dropdown-menu",
      flag_descriptions::kMouseAndTrackpadDropdownMenuName,
      flag_descriptions::kMouseAndTrackpadDropdownMenuDescription, kOsAndroid,
diff --git a/chrome/browser/accessibility/DEPS b/chrome/browser/accessibility/DEPS
index 38df7f9..5697e212 100644
--- a/chrome/browser/accessibility/DEPS
+++ b/chrome/browser/accessibility/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+components/live_caption",
   "+components/soda",
-  "+pdf/pdf_features.h",
   "+services/image_annotation",
   "+testing/gmock",
   "+testing/gtest",
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
index a93e810..28453d340 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
@@ -546,6 +546,8 @@
           title_indicator_layer->children()[0].get(), title_layer->layer());
     }
     title_layer->SetUIResourceIds();
+  } else {
+    title_indicator_layer->RemoveAllChildren();
   }
 
   // Set bottom indicator properties.
diff --git a/chrome/browser/apps/guest_view/DEPS b/chrome/browser/apps/guest_view/DEPS
deleted file mode 100644
index 4d41653..0000000
--- a/chrome/browser/apps/guest_view/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-specific_include_rules = {
-    "web_view_browsertest\.cc": [
-        "+pdf/pdf_features.h",
-    ],
-}
\ No newline at end of file
diff --git a/chrome/browser/apps/link_capturing/link_capturing_navigation_throttle_browsertest.cc b/chrome/browser/apps/link_capturing/link_capturing_navigation_throttle_browsertest.cc
index 865004e..4a3d326 100644
--- a/chrome/browser/apps/link_capturing/link_capturing_navigation_throttle_browsertest.cc
+++ b/chrome/browser/apps/link_capturing/link_capturing_navigation_throttle_browsertest.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/apps/link_capturing/link_capturing_navigation_throttle.h"
 
-#include "base/strings/stringprintf.h"
-
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -16,6 +14,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_test_observers.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -92,7 +91,7 @@
   webapps::AppId app_id_;
   base::test::ScopedFeatureList feature_list_{
       features::kDesktopPWAsLinkCapturing};
-  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 IN_PROC_BROWSER_TEST_F(LinkCapturingNavigationThrottleBrowserTest,
diff --git a/chrome/browser/ash/browser_context_keyed_service_factories.cc b/chrome/browser/ash/browser_context_keyed_service_factories.cc
index 1576fa66..07f2ca2 100644
--- a/chrome/browser/ash/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/ash/browser_context_keyed_service_factories.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/ash/child_accounts/child_user_service_factory.h"
 #include "chrome/browser/ash/child_accounts/event_based_status_reporting_service_factory.h"
 #include "chrome/browser/ash/child_accounts/family_user_metrics_service_factory.h"
+#include "chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.h"
 #include "chrome/browser/ash/child_accounts/screen_time_controller_factory.h"
 #include "chrome/browser/ash/concierge_helper_service.h"
 #include "chrome/browser/ash/crosapi/keystore_service_factory_ash.h"
@@ -217,6 +218,7 @@
   nearby::NearbyProcessManagerFactory::GetInstance();
   nearby::presence::NearbyPresenceServiceFactory::GetInstance();
   OAuth2LoginManagerFactory::GetInstance();
+  on_device_controls::AppControlsServiceFactory::GetInstance();
   OfflineSigninLimiterFactory::GetInstance();
   OwnerSettingsServiceAshFactory::GetInstance();
   PasswordSyncTokenVerifierFactory::GetInstance();
diff --git a/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.cc b/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.cc
index 0aff72b..adab27f 100644
--- a/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.cc
+++ b/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.cc
@@ -4,9 +4,18 @@
 
 #include "chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.h"
 
+#include "ash/constants/ash_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+
 namespace ash {
 namespace on_device_controls {
 
+// static
+void AppControlsService::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(prefs::kOnDeviceAppControlsSetupCompleted,
+                                false);
+}
+
 AppControlsService::AppControlsService() = default;
 
 AppControlsService::~AppControlsService() = default;
diff --git a/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.h b/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.h
index c0741196..641509ea 100644
--- a/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.h
+++ b/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service.h
@@ -7,6 +7,8 @@
 
 #include "components/keyed_service/core/keyed_service.h"
 
+class PrefRegistrySimple;
+
 namespace ash {
 namespace on_device_controls {
 
@@ -14,6 +16,8 @@
 // blocking apps.
 class AppControlsService : public KeyedService {
  public:
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
   AppControlsService();
   ~AppControlsService() override;
   AppControlsService(const AppControlsService&) = delete;
diff --git a/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.cc b/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.cc
index fc8fe69..bc70baa 100644
--- a/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.cc
+++ b/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ash/child_accounts/on_device_controls/on_device_utils.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 
 namespace {
 constexpr char kServiceName[] = "AppsControlsService";
@@ -64,5 +65,10 @@
   return std::make_unique<AppControlsService>();
 }
 
+void AppControlsServiceFactory::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  AppControlsService::RegisterProfilePrefs(registry);
+}
+
 }  // namespace on_device_controls
 }  // namespace ash
diff --git a/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.h b/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.h
index 933b2d9..7ed9873 100644
--- a/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.h
+++ b/chrome/browser/ash/child_accounts/on_device_controls/app_controls_service_factory.h
@@ -16,6 +16,10 @@
 class BrowserContext;
 }  // namespace content
 
+namespace user_prefs {
+class PrefRegistrySyncable;
+}  // namespace user_prefs
+
 namespace ash {
 namespace on_device_controls {
 
@@ -47,6 +51,8 @@
   // BrowserContextKeyedServiceFactory:
   std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
       content::BrowserContext* context) const override;
+  void RegisterProfilePrefs(
+      user_prefs::PrefRegistrySyncable* registry) override;
 };
 
 }  // namespace on_device_controls
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.cc b/chrome/browser/ash/crosapi/test_controller_ash.cc
index 379b8a0f..918a5191 100644
--- a/chrome/browser/ash/crosapi/test_controller_ash.cc
+++ b/chrome/browser/ash/crosapi/test_controller_ash.cc
@@ -150,6 +150,21 @@
   waiter.Wait();
 }
 
+std::string GetMachineStatisticKeyString(mojom::MachineStatisticKeyType key) {
+  if (key == mojom::MachineStatisticKeyType::kOemDeviceRequisitionKey) {
+    return ash::system::kOemDeviceRequisitionKey;
+  }
+  if (key == mojom::MachineStatisticKeyType::kHardwareClassKey) {
+    return ash::system::kHardwareClassKey;
+  }
+  if (key == mojom::MachineStatisticKeyType::kCustomizationIdKey) {
+    return ash::system::kCustomizationIdKey;
+  }
+
+  // Return empty string for unknown key.
+  return "";
+}
+
 const base::TimeDelta kWindowWaitTimeout = base::Seconds(10);
 
 TestControllerAsh* g_instance = nullptr;
@@ -1043,6 +1058,34 @@
   std::move(callback).Run();
 }
 
+void TestControllerAsh::EnableStatisticsProviderForTesting(
+    bool enable,
+    EnableStatisticsProviderForTestingCallback callback) {
+  ash::system::StatisticsProvider::SetTestProvider(
+      enable ? &fake_statistics_provider_ : nullptr);
+  std::move(callback).Run();
+}
+
+void TestControllerAsh::ClearAllMachineStatistics(
+    ClearAllMachineStatisticsCallback callback) {
+  fake_statistics_provider_.ClearAllMachineStatistics();
+  std::move(callback).Run();
+}
+
+void TestControllerAsh::SetMachineStatistic(
+    mojom::MachineStatisticKeyType key,
+    const std::string& value,
+    SetMachineStatisticCallback callback) {
+  std::string key_string = GetMachineStatisticKeyString(key);
+  if (!key_string.empty()) {
+    fake_statistics_provider_.SetMachineStatistic(key_string, value);
+    std::move(callback).Run(true);
+  } else {
+    LOG(WARNING) << "Unknown key for setting machine statistic";
+    std::move(callback).Run(false);
+  }
+}
+
 // This class waits for overview mode to either enter or exit and fires a
 // callback. This class will fire the callback at most once.
 class TestControllerAsh::OverviewWaiter : public ash::OverviewObserver {
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.h b/chrome/browser/ash/crosapi/test_controller_ash.h
index 6bc7f497..73434cc 100644
--- a/chrome/browser/ash/crosapi/test_controller_ash.h
+++ b/chrome/browser/ash/crosapi/test_controller_ash.h
@@ -14,6 +14,7 @@
 #include "ash/wm/splitview/split_view_types.h"
 #include "base/one_shot_event.h"
 #include "chrome/browser/ash/crosapi/crosapi_ash.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/crosapi/mojom/test_controller.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -170,6 +171,17 @@
   void UpdateDisplay(int number_of_displays,
                      UpdateDisplayCallback callback) override;
 
+  void EnableStatisticsProviderForTesting(
+      bool enable,
+      EnableStatisticsProviderForTestingCallback callback) override;
+
+  void ClearAllMachineStatistics(
+      ClearAllMachineStatisticsCallback callback) override;
+
+  void SetMachineStatistic(mojom::MachineStatisticKeyType key,
+                           const std::string& value,
+                           SetMachineStatisticCallback callback) override;
+
   mojom::StandaloneBrowserTestController* GetStandaloneBrowserTestController() {
     DCHECK(standalone_browser_test_controller_.is_bound());
     return standalone_browser_test_controller_.get();
@@ -227,6 +239,8 @@
   // Ash utterance event delegates by utterance id.
   std::map<int, std::unique_ptr<AshUtteranceEventDelegate>>
       ash_utterance_event_delegates_;
+
+  ash::system::FakeStatisticsProvider fake_statistics_provider_;
 };
 
 class TestShillControllerAsh : public crosapi::mojom::TestShillController {
diff --git a/chrome/browser/ash/file_manager/DEPS b/chrome/browser/ash/file_manager/DEPS
index 013175cb..4534fa6 100644
--- a/chrome/browser/ash/file_manager/DEPS
+++ b/chrome/browser/ash/file_manager/DEPS
@@ -101,6 +101,5 @@
 specific_include_rules = {
   "file_manager_browsertest_base.cc": [
     "+chrome/browser/ui/views/select_file_dialog_extension.h",
-    "+pdf/pdf_features.h",
   ],
 }
diff --git a/chrome/browser/ash/growth/DEPS b/chrome/browser/ash/growth/DEPS
index 7f5608c5..4ab9f7b 100644
--- a/chrome/browser/ash/growth/DEPS
+++ b/chrome/browser/ash/growth/DEPS
@@ -42,4 +42,10 @@
 
   # Dependencies outside of //chrome:
   "+ui/message_center/message_center.h",
-]
\ No newline at end of file
+]
+
+specific_include_rules = {
+  "campaigns_manager_interactive_uitest\.cc": [
+    "+chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h",
+  ],
+}
diff --git a/chrome/browser/ash/growth/campaigns_manager_interactive_uitest.cc b/chrome/browser/ash/growth/campaigns_manager_interactive_uitest.cc
index 668e470..d561a29 100644
--- a/chrome/browser/ash/growth/campaigns_manager_interactive_uitest.cc
+++ b/chrome/browser/ash/growth/campaigns_manager_interactive_uitest.cc
@@ -4,6 +4,8 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
+#include "ash/system/toast/system_nudge_view.h"
+#include "ash/webui/system_apps/public/system_web_app_type.h"
 #include "base/callback_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -11,12 +13,15 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "chrome/test/base/chromeos/crosier/interactive_ash_test.h"
 #include "chromeos/ash/components/growth/campaigns_constants.h"
 #include "chromeos/ash/components/growth/campaigns_manager.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/test/mock_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/aura/env.h"
+#include "ui/base/interaction/interactive_test.h"
 
 namespace {
 
@@ -27,12 +32,62 @@
 }
 )";
 
+// Targeting Personalization App.
+constexpr char kCampaignsNudge[] = R"(
+{
+  "2": [
+    {
+      "id": 100,
+      "targetings": [
+        {
+          "runtime": {
+            "appsOpened": [
+              {"appId": "glenkcidjgckcomnliblmkokolehpckn"}
+            ]
+          }
+        }
+      ],
+      "payload": {
+        "nudge": {
+          "title": "Title",
+          "body": "Body",
+          "duration": 2,
+          "image": {
+            "builtInIcon": 0
+          },
+          "arrow": 1,
+          "anchor": {
+            "activeAppWindowAnchorType": 0
+          },
+          "primaryButton": {
+            "label": "Yes",
+            "action": {
+              "type": 3,
+              "params": {
+                "url": "https://www.google.com",
+                "disposition": 0
+              }
+            }
+          },
+          "secondaryButton": {
+            "label": "No",
+            "action": {}
+          }
+        }
+      }
+    }
+  ]
+}
+)";
+
 base::FilePath GetCampaignsFilePath(const base::ScopedTempDir& dir) {
   return dir.GetPath().Append(kCampaignsFileName);
 }
 
 }  // namespace
 
+// CampaignsManagerInteractiveUiTest -------------------------------------------
+
 class CampaignsManagerInteractiveUiTest : public InteractiveAshTest {
  public:
   CampaignsManagerInteractiveUiTest() {
@@ -88,9 +143,10 @@
             GetActiveUserProfile()));
   }
 
+  base::ScopedTempDir temp_dir_;
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
-  base::ScopedTempDir temp_dir_;
   base::CallbackListSubscription create_services_subscription_;
   base::WeakPtrFactory<CampaignsManagerInteractiveUiTest> weak_ptr_factory_{
       this};
@@ -132,3 +188,76 @@
   growth::CampaignsManager::Get()->ClearEvent(growth::CampaignEvent::kAppOpened,
                                               "abcd");
 }
+
+// CampaignsManagerInteractiveUiNudgeTest ----------------------------------
+
+class CampaignsManagerInteractiveUiNudgeTest
+    : public CampaignsManagerInteractiveUiTest {
+ public:
+  CampaignsManagerInteractiveUiNudgeTest() {
+    base::WriteFile(GetCampaignsFilePath(temp_dir_), kCampaignsNudge);
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    InteractiveAshTest::SetupContextWidget();
+
+    // Use SWA as targets and anchors in the tests.
+    InstallSystemApps();
+  }
+
+ protected:
+  auto LaunchSystemWebApp(ash::SystemWebAppType type) {
+    return Do(
+        [=]() { ash::LaunchSystemWebAppAsync(GetActiveUserProfile(), type); });
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(CampaignsManagerInteractiveUiNudgeTest,
+                       AnchorPersonalizationApp) {
+  aura::Env* env = aura::Env::GetInstance();
+  ASSERT_TRUE(env);
+
+  RunTestSequence(InAnyContext(Steps(LaunchSystemWebApp(
+                      ash::SystemWebAppType::PERSONALIZATION))),
+                  WaitForWindowWithTitle(env, u"Wallpaper & style"),
+                  WaitForShow(ash::SystemNudgeView::kBubbleIdForTesting));
+}
+
+IN_PROC_BROWSER_TEST_F(CampaignsManagerInteractiveUiNudgeTest,
+                       NotShowOnSettingsApp) {
+  aura::Env* env = aura::Env::GetInstance();
+  ASSERT_TRUE(env);
+
+  RunTestSequence(
+      InAnyContext(Steps(LaunchSystemWebApp(ash::SystemWebAppType::SETTINGS))),
+      WaitForWindowWithTitle(env, u"Settings"),
+      EnsureNotPresent(ash::SystemNudgeView::kBubbleIdForTesting));
+}
+
+IN_PROC_BROWSER_TEST_F(CampaignsManagerInteractiveUiNudgeTest,
+                       ClickPrimaryButtonInAnchoredNudge) {
+  aura::Env* env = aura::Env::GetInstance();
+  ASSERT_TRUE(env);
+
+  RunTestSequence(
+      InAnyContext(
+          Steps(LaunchSystemWebApp(ash::SystemWebAppType::PERSONALIZATION))),
+      WaitForShow(ash::SystemNudgeView::kBubbleIdForTesting), FlushEvents(),
+      PressButton(ash::SystemNudgeView::kPrimaryButtonIdForTesting),
+      WaitForHide(ash::SystemNudgeView::kBubbleIdForTesting),
+      WaitForWindowWithTitle(env, u"www.google.com"));
+}
+
+IN_PROC_BROWSER_TEST_F(CampaignsManagerInteractiveUiNudgeTest,
+                       ClickSecondaryButtonInAnchoredNudge) {
+  aura::Env* env = aura::Env::GetInstance();
+  ASSERT_TRUE(env);
+
+  RunTestSequence(
+      InAnyContext(
+          Steps(LaunchSystemWebApp(ash::SystemWebAppType::PERSONALIZATION))),
+      WaitForShow(ash::SystemNudgeView::kBubbleIdForTesting), FlushEvents(),
+      PressButton(ash::SystemNudgeView::kSecondaryButtonIdForTesting),
+      WaitForHide(ash::SystemNudgeView::kBubbleIdForTesting));
+}
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index c0b450e..536c552 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -1244,7 +1244,29 @@
                    ->clear_forced_re_enrollment_vpd_call_count());
 }
 
-IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
+class WizardControllerDeviceLegacyTest
+    : public WizardControllerDeviceStateTest {
+ public:
+  WizardControllerDeviceLegacyTest(
+      const WizardControllerDeviceLegacyTest&) = delete;
+  WizardControllerDeviceLegacyTest& operator=(
+      const WizardControllerDeviceLegacyTest&) = delete;
+
+ protected:
+  WizardControllerDeviceLegacyTest() = default;
+  ~WizardControllerDeviceLegacyTest() override = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    WizardControllerDeviceStateTest::SetUpCommandLine(command_line);
+
+    // Explicitly test legacy state determination flow.
+    command_line->AppendSwitchASCII(
+        switches::kEnterpriseEnableUnifiedStateDetermination,
+        policy::AutoEnrollmentTypeChecker::kUnifiedStateDeterminationNever);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(WizardControllerDeviceLegacyTest,
                        ServerAdvertisedReEnrollment) {
   base::Value::Dict device_state;
   device_state.Set(
@@ -1277,7 +1299,7 @@
 #else
 #define MAYBE_ControlFlowDeviceDisabled ControlFlowDeviceDisabled
 #endif
-IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
+IN_PROC_BROWSER_TEST_F(WizardControllerDeviceLegacyTest,
                        MAYBE_ControlFlowDeviceDisabled) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
@@ -1710,6 +1732,82 @@
   CheckCurrentScreen(DeviceDisabledScreenView::kScreenId);
 }
 
+// Tests that when EnrollmentStateFetcher reports kDisabled and configures
+// corresponding device state mode, we show device disabled screen after
+// attempting to enter demo mode.
+IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest,
+                       DisabledDemoMode) {
+  ScopedEnrollmentStateFetcherFactory fetcher_factory(
+      auto_enrollment_controller());
+
+  CheckCurrentScreen(WelcomeView::kScreenId);
+  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
+
+  EXPECT_CALL(*mock_welcome_screen_, HideImpl());
+  EXPECT_CALL(*mock_network_screen_, ShowImpl());
+
+  WizardController::default_controller()->StartDemoModeSetup();
+
+  CheckCurrentScreen(NetworkScreenView::kScreenId);
+  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
+
+  EXPECT_CALL(*mock_network_screen_, HideImpl());
+  EXPECT_CALL(*mock_demo_preferences_screen_, ShowImpl());
+
+  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);
+
+  CheckCurrentScreen(DemoPreferencesScreenView::kScreenId);
+  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
+
+  EXPECT_CALL(*mock_demo_preferences_screen_, HideImpl());
+  EXPECT_CALL(*mock_update_screen_, ShowImpl());
+
+  mock_demo_preferences_screen_->ExitScreen(
+      DemoPreferencesScreen::Result::COMPLETED);
+
+  // Let update screen smooth time process (time = 0ms).
+  base::RunLoop().RunUntilIdle();
+
+  CheckCurrentScreen(UpdateView::kScreenId);
+  EXPECT_CALL(*mock_update_screen_, HideImpl());
+  EXPECT_CALL(*mock_consolidated_consent_screen_, ShowImpl());
+
+  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);
+
+  CheckCurrentScreen(ConsolidatedConsentScreenView::kScreenId);
+  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
+
+  EXPECT_CALL(*mock_consolidated_consent_screen_, HideImpl());
+  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl());
+
+  mock_consolidated_consent_screen_->ExitScreen(
+      ConsolidatedConsentScreen::Result::ACCEPTED_DEMO_ONLINE);
+
+  CheckCurrentScreen(AutoEnrollmentCheckScreenView::kScreenId);
+  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
+
+  mock_auto_enrollment_check_screen_->RealShow();
+
+  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();
+
+  base::Value::Dict device_state;
+  device_state.Set(policy::kDeviceStateMode,
+                   base::Value(policy::kDeviceStateModeDisabled));
+  device_state.Set(policy::kDeviceStateDisabledMessage,
+                   base::Value(kDisabledMessage));
+  g_browser_process->local_state()->SetDict(prefs::kServerBackedDeviceState,
+                                            std::move(device_state));
+
+  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl());
+  EXPECT_CALL(*device_disabled_screen_view_,
+              Show(_, _, kDisabledMessage, false));
+
+  fetcher_factory.ReportEnrollmentState(
+      policy::AutoEnrollmentResult::kDisabled);
+
+  CheckCurrentScreen(DeviceDisabledScreenView::kScreenId);
+}
+
 // Tests that when EnrollmentStateFetcher times out, we set state correctly,
 // show an error on enrollment check screen and that it is not possible to enter
 // guest mode (like in FRE).
@@ -1779,6 +1877,11 @@
     command_line->AppendSwitchASCII(
         switches::kEnterpriseEnableInitialEnrollment,
         policy::AutoEnrollmentTypeChecker::kInitialEnrollmentAlways);
+
+    // Explicitly test legacy state determination flow.
+    command_line->AppendSwitchASCII(
+        switches::kEnterpriseEnableUnifiedStateDetermination,
+        policy::AutoEnrollmentTypeChecker::kUnifiedStateDeterminationNever);
   }
 
   // Test initial enrollment. This method is shared by the tests for initial
@@ -2750,6 +2853,15 @@
   WizardControllerDemoSetupDeviceDisabledTest() = default;
   ~WizardControllerDemoSetupDeviceDisabledTest() override = default;
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    WizardControllerDeviceStateTest::SetUpCommandLine(command_line);
+
+    // Explicitly test legacy state determination flow.
+    command_line->AppendSwitchASCII(
+        switches::kEnterpriseEnableUnifiedStateDetermination,
+        policy::AutoEnrollmentTypeChecker::kUnifiedStateDeterminationNever);
+  }
+
   // MixinBasedInProcessBrowserTest:
   void SetUpOnMainThread() override {
     WizardControllerDeviceStateTest::SetUpOnMainThread();
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
index d54a5fe..69ac9ba 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker_unittest.cc
@@ -730,7 +730,7 @@
       AutoEnrollmentTypeChecker::kUnifiedStateDeterminationOfficialBuild);
 
   EXPECT_EQ(AutoEnrollmentTypeChecker::IsUnifiedStateDeterminationEnabled(),
-            !kill_switch_enabled_ && google_branded_);
+            !kill_switch_enabled_ && IsOfficialGoogleOS());
 }
 
 TEST_P(AutoEnrollmentTypeCheckerUnifiedStateDeterminationTestP, Never) {
diff --git a/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h b/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h
index cb67596..ffaadf7 100644
--- a/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h
+++ b/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h
@@ -109,8 +109,6 @@
                                   bool wait_for_load,
                                   Browser** out_browser);
 
-  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
-
   std::unique_ptr<TestSystemWebAppInstallation> installation_ = nullptr;
 };
 
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 79848ac6..fc23585 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
@@ -1161,6 +1161,16 @@
         ResettersForTesting.register(() -> this.mImageFetcher = oldValue);
     }
 
+    /** Sets the preference value for supporting payments using Pix. */
+    public void setFacilitatedPaymentsPixPref(boolean value) {
+        mPrefService.setBoolean(Pref.FACILITATED_PAYMENTS_PIX, value);
+    }
+
+    /** Returns the preference value for supporting payments using Pix. */
+    public boolean getFacilitatedPaymentsPixPref() {
+        return mPrefService.getBoolean(Pref.FACILITATED_PAYMENTS_PIX);
+    }
+
     @NativeMethods
     interface Natives {
         long init(PersonalDataManager caller, @JniType("Profile*") Profile profile);
diff --git a/chrome/browser/banners/android/ambient_badge_manager_browsertest.cc b/chrome/browser/banners/android/ambient_badge_manager_browsertest.cc
index d062209..9aefb85 100644
--- a/chrome/browser/banners/android/ambient_badge_manager_browsertest.cc
+++ b/chrome/browser/banners/android/ambient_badge_manager_browsertest.cc
@@ -163,152 +163,15 @@
 
   ~AmbientBadgeManagerBrowserTest() override = default;
 
-  void SetUp() override {
-    SetUpFeatureList();
-    AndroidBrowserTest::SetUp();
-  }
-
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
     site_engagement::SiteEngagementScore::SetParamValuesForTesting();
 
+    app_banner_manager_ = std::make_unique<TestAppBannerManager>(
+        web_contents(), &mock_segmentation_service_);
     AndroidBrowserTest::SetUpOnMainThread();
   }
 
- protected:
-  content::WebContents* web_contents() {
-    return chrome_test_utils::GetActiveWebContents(this);
-  }
-
-  virtual void SetUpFeatureList() {
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{},
-        /*disabled_features=*/{features::kAmbientBadgeSuppressFirstVisit,
-                               features::kInstallPromptSegmentation});
-  }
-
-  void ResetEngagementForUrl(const GURL& url, double score) {
-    site_engagement::SiteEngagementService* service =
-        site_engagement::SiteEngagementService::Get(
-            Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-    service->ResetBaseScoreForURL(url, score);
-  }
-
-  void RunTest(TestAppBannerManager* app_banner_manager,
-               const GURL& url,
-               AmbientBadgeManager::State expected_state) {
-    ResetEngagementForUrl(url, 10);
-
-    base::RunLoop waiter;
-
-    app_banner_manager->WaitForAmbientBadgeState(expected_state,
-                                                 waiter.QuitClosure());
-    ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
-
-    waiter.Run();
-  }
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-
- private:
-  // Disable the banners in the browser so it won't interfere with the test.
-  base::AutoReset<bool> disable_banner_trigger_;
-};
-
-IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest, ShowAmbientBadge) {
-  auto app_banner_manager =
-      std::make_unique<TestAppBannerManager>(web_contents());
-
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
-          AmbientBadgeManager::State::kShowing);
-}
-
-IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest, NoServiceWorker) {
-  auto app_banner_manager =
-      std::make_unique<TestAppBannerManager>(web_contents());
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL(
-              "/banners/manifest_no_service_worker.html"),
-          AmbientBadgeManager::State::kPendingWorker);
-}
-
-IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest,
-                       BlockedIfDismissRecently) {
-  auto app_banner_manager =
-      std::make_unique<TestAppBannerManager>(web_contents());
-
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
-          AmbientBadgeManager::State::kShowing);
-
-  // Explicitly dismiss the badge.
-  app_banner_manager->GetBadgeManagerForTest()->BadgeDismissed();  // IN-TEST
-  EXPECT_EQ(AmbientBadgeManager::State::kDismissed,
-            app_banner_manager->GetBadgeManagerForTest()->state());  // IN-TEST
-
-  // Badge blocked because it was recently dismissed.
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
-          AmbientBadgeManager::State::kBlocked);
-
-  // Badge can show again after 91 days.
-  webapps::AppBannerManager::SetTimeDeltaForTesting(91);
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
-          AmbientBadgeManager::State::kShowing);
-}
-
-class AmbientBadgeManagerSecondVisitTest
-    : public AmbientBadgeManagerBrowserTest {
- public:
-  AmbientBadgeManagerSecondVisitTest() = default;
-
-  AmbientBadgeManagerSecondVisitTest(
-      const AmbientBadgeManagerSecondVisitTest&) = delete;
-  AmbientBadgeManagerSecondVisitTest& operator=(
-      const AmbientBadgeManagerSecondVisitTest&) = delete;
-
-  ~AmbientBadgeManagerSecondVisitTest() override = default;
-
-  void SetUpFeatureList() override {
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kAmbientBadgeSuppressFirstVisit},
-        /*disabled_features=*/{features::kInstallPromptSegmentation});
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerSecondVisitTest,
-                       SuppressedOnFirstVisit) {
-  auto app_banner_manager =
-      std::make_unique<TestAppBannerManager>(web_contents());
-
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
-          AmbientBadgeManager::State::kPendingEngagement);
-
-  // Load again, can show.
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
-          AmbientBadgeManager::State::kShowing);
-}
-
-class AmbientBadgeManagerSmartTest : public AmbientBadgeManagerBrowserTest {
- public:
-  AmbientBadgeManagerSmartTest() = default;
-
-  AmbientBadgeManagerSmartTest(const AmbientBadgeManagerSmartTest&) = delete;
-  AmbientBadgeManagerSmartTest& operator=(const AmbientBadgeManagerSmartTest&) =
-      delete;
-
-  ~AmbientBadgeManagerSmartTest() override = default;
-
-  void SetUpFeatureList() override {
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kInstallPromptSegmentation},
-        /*disabled_features=*/{features::kAmbientBadgeSuppressFirstVisit});
-  }
-
   segmentation_platform::ClassificationResult GetClassificationResult(
       std::string label) {
     segmentation_platform::ClassificationResult result(
@@ -317,65 +180,90 @@
     return result;
   }
 
+ protected:
+  content::WebContents* web_contents() {
+    return chrome_test_utils::GetActiveWebContents(this);
+  }
+
+  AmbientBadgeManager* GetAmbientBadgeManager() {
+    return app_banner_manager_->GetBadgeManagerForTest();
+  }
+
+  void ResetEngagementForUrl(const GURL& url, double score) {
+    site_engagement::SiteEngagementService* service =
+        site_engagement::SiteEngagementService::Get(
+            Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
+    service->ResetBaseScoreForURL(url, score);
+  }
+
+  void SetSegmentationResult(std::string label) {
+    EXPECT_CALL(mock_segmentation_service_, GetClassificationResult(_, _, _, _))
+        .WillOnce(RunOnceCallback<3>(GetClassificationResult(label)));
+  }
+
+  void RunTest(const GURL& url, AmbientBadgeManager::State expected_state) {
+    ResetEngagementForUrl(url, 10);
+
+    base::RunLoop waiter;
+
+    app_banner_manager_->WaitForAmbientBadgeState(expected_state,
+                                                  waiter.QuitClosure());
+    ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+    waiter.Run();
+  }
+
+ private:
+  // Disable the banners in the browser so it won't interfere with the test.
+  base::AutoReset<bool> disable_banner_trigger_;
+
+  std::unique_ptr<TestAppBannerManager> app_banner_manager_;
   segmentation_platform::MockSegmentationPlatformService
       mock_segmentation_service_;
 };
 
-IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerSmartTest, ShowInstallMessages) {
-  EXPECT_CALL(mock_segmentation_service_, GetClassificationResult(_, _, _, _))
-      .WillOnce(RunOnceCallback<3>(GetClassificationResult(
-          MLInstallabilityPromoter::kShowInstallPromptLabel)));
+IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest, ShowAmbientBadge) {
+  SetSegmentationResult(MLInstallabilityPromoter::kShowInstallPromptLabel);
 
-  auto app_banner_manager = std::make_unique<TestAppBannerManager>(
-      web_contents(), &mock_segmentation_service_);
-
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
+  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
           AmbientBadgeManager::State::kShowing);
 }
 
-IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerSmartTest,
+IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest,
                        BlockedBySegmentationResult) {
-  EXPECT_CALL(mock_segmentation_service_, GetClassificationResult(_, _, _, _))
-      .WillOnce(RunOnceCallback<3>(GetClassificationResult("DontShow")));
+  SetSegmentationResult(MLInstallabilityPromoter::kDontShowLabel);
 
-  auto app_banner_manager = std::make_unique<TestAppBannerManager>(
-      web_contents(), &mock_segmentation_service_);
-
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
-          AmbientBadgeManager::State::kSegmentation);
+  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
+          AmbientBadgeManager::State::kSegmentationBlock);
 }
 
-IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerSmartTest, BlockedByGuardrail) {
-  EXPECT_CALL(mock_segmentation_service_, GetClassificationResult(_, _, _, _))
-      .Times(testing::Exactly(2))
-      .WillRepeatedly(
-          base::test::RunOnceCallbackRepeatedly<3>(GetClassificationResult(
-              MLInstallabilityPromoter::kShowInstallPromptLabel)));
+IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest, NoServiceWorker) {
+  SetSegmentationResult(MLInstallabilityPromoter::kShowInstallPromptLabel);
 
-  auto app_banner_manager = std::make_unique<TestAppBannerManager>(
-      web_contents(), &mock_segmentation_service_);
+  RunTest(embedded_test_server()->GetURL(
+              "/banners/manifest_no_service_worker.html"),
+          AmbientBadgeManager::State::kShowing);
+}
 
-  // Showing OK for the first visit
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
+IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest,
+                       BlockedIfDismissRecently) {
+  SetSegmentationResult(MLInstallabilityPromoter::kShowInstallPromptLabel);
+  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
           AmbientBadgeManager::State::kShowing);
 
   // Explicitly dismiss the badge.
-  app_banner_manager->GetBadgeManagerForTest()->BadgeDismissed();  // IN-TEST
+  GetAmbientBadgeManager()->BadgeDismissed();  // IN-TEST
   EXPECT_EQ(AmbientBadgeManager::State::kDismissed,
-            app_banner_manager->GetBadgeManagerForTest()->state());  // IN-TEST
+            GetAmbientBadgeManager()->state());  // IN-TEST
 
   // Badge blocked because it was recently dismissed.
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
+  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
           AmbientBadgeManager::State::kBlocked);
 
   // Badge can show again after 91 days.
+  SetSegmentationResult(MLInstallabilityPromoter::kShowInstallPromptLabel);
   webapps::AppBannerManager::SetTimeDeltaForTesting(91);
-  RunTest(app_banner_manager.get(),
-          embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
+  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
           AmbientBadgeManager::State::kShowing);
 }
 
diff --git a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AmbientBadgeManagerTest.java b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AmbientBadgeManagerTest.java
index 46fdaf74..9fa43c28 100644
--- a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AmbientBadgeManagerTest.java
+++ b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AmbientBadgeManagerTest.java
@@ -43,15 +43,12 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.base.test.util.PackageManagerWrapper;
 import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.tab.Tab;
@@ -86,7 +83,6 @@
 /** Tests the app banners. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@DisableFeatures({ChromeFeatureList.WEB_APP_AMBIENT_BADGE_SUPRESS_FIRST_VISIT})
 public class AmbientBadgeManagerTest {
     @Rule
     public ChromeTabbedActivityTestRule mTabbedActivityTestRule =
@@ -199,6 +195,7 @@
 
         AppBannerManager.ignoreChromeChannelForTesting();
         AppBannerManager.setTotalEngagementForTesting(10);
+        AppBannerManager.setOverrideSegmentationResultForTesting(true);
         mTestServer =
                 EmbeddedTestServer.createAndStartServer(
                         ApplicationProvider.getApplicationContext());
@@ -399,7 +396,8 @@
 
         triggerInstallWebApp(
                 mTabbedActivityTestRule,
-                WebappTestPage.getServiceWorkerUrlWithAction(mTestServer, "verify_appinstalled"));
+                WebappTestPage.getNonServiceWorkerUrlWithAction(
+                        mTestServer, "verify_appinstalled"));
 
         // The appinstalled event should fire (and cause the title to change).
         new TabTitleObserver(
@@ -427,7 +425,8 @@
                         ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL));
         triggerInstallWebApp(
                 mCustomTabActivityTestRule,
-                WebappTestPage.getServiceWorkerUrlWithAction(mTestServer, "verify_appinstalled"));
+                WebappTestPage.getNonServiceWorkerUrlWithAction(
+                        mTestServer, "verify_appinstalled"));
 
         // The appinstalled event should fire (and cause the title to change).
         new TabTitleObserver(
@@ -481,7 +480,7 @@
                 HistogramWatcher.newBuilder().expectNoRecords(INSTALL_PATH_HISTOGRAM_NAME).build();
 
         // Visit a site that is a PWA. The ambient badge should show.
-        String webBannerUrl = WebappTestPage.getServiceWorkerUrl(mTestServer);
+        String webBannerUrl = WebappTestPage.getNonServiceWorkerUrl(mTestServer);
         resetEngagementForUrl(webBannerUrl, 10);
         Tab tab = mTabbedActivityTestRule.getActivity().getActivityTab();
         new TabLoadObserver(tab).fullyLoadUrl(webBannerUrl);
@@ -544,9 +543,7 @@
     @Test
     @MediumTest
     public void testAmbientBadgeAppearWithServiceWorkerPage() throws Exception {
-        String webBannerUrl =
-                WebappTestPage.getServiceWorkerUrlWithAction(
-                        mTestServer, "call_stashed_prompt_on_click");
+        String webBannerUrl = WebappTestPage.getNonServiceWorkerUrl(mTestServer);
         resetEngagementForUrl(webBannerUrl, 10);
         navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, webBannerUrl);
 
@@ -560,9 +557,7 @@
     public void testAmbientBadgeTriggeredWithListedRelatedApp() throws Exception {
         // The ambient badge should show if there is play app in related applications list but
         // preferred_related_applications is false.
-        String webBannerUrl =
-                WebappTestPage.getServiceWorkerUrlWithAction(
-                        mTestServer, "call_stashed_prompt_on_click");
+        String webBannerUrl = WebappTestPage.getNonServiceWorkerUrl(mTestServer);
         resetEngagementForUrl(webBannerUrl, 10);
         navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, webBannerUrl);
 
@@ -596,10 +591,8 @@
     @SmallTest
     public void testAmbientBadgeDoesNotAppearWhenRelatedAppInstalled() throws Exception {
         String url =
-                WebappTestPage.getServiceWorkerUrlWithManifestAndAction(
-                        mTestServer,
-                        WEB_APP_MANIFEST_WITH_RELATED_APP_LIST,
-                        "call_stashed_prompt_on_click");
+                WebappTestPage.getNonServiceWorkerUrlWithManifest(
+                        mTestServer, WEB_APP_MANIFEST_WITH_RELATED_APP_LIST);
         resetEngagementForUrl(url, 10);
 
         final Context contextToRestore = ContextUtils.getApplicationContext();
@@ -617,32 +610,28 @@
 
     @Test
     @SmallTest
-    @EnableFeatures({ChromeFeatureList.WEB_APP_AMBIENT_BADGE_SUPRESS_FIRST_VISIT})
-    public void testAmbientBadgeSuppressedOnFirstVisit() throws Exception {
-        String url =
-                WebappTestPage.getServiceWorkerUrlWithAction(
-                        mTestServer, "call_stashed_prompt_on_click");
+    public void testMlShowAmbientBadge() throws Exception {
+        String url = WebappTestPage.getNonServiceWorkerUrl(mTestServer);
         resetEngagementForUrl(url, 10);
+        AppBannerManager.setOverrideSegmentationResultForTesting(false);
 
         navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, url);
 
         assertAppBannerPipelineStatus(AppBannerManagerState.PENDING_PROMPT_NOT_CANCELED);
 
         Tab tab = mTabbedActivityTestRule.getActivity().getActivityTab();
-        waitForBadgeStatus(tab, AmbientBadgeState.PENDING_ENGAGEMENT);
 
-        // Advance 3 days and navigate to |url| again, ambient badge should show.
+        // Blocked by segmentation result.
+        waitForBadgeStatus(tab, AmbientBadgeState.SEGMENTATION_BLOCK);
+        checkAmbientBadgePromptNotExist(mTabbedActivityTestRule);
+
+        // Advance 3 days and navigate to |url| again
         AppBannerManager.setTimeDeltaForTesting(3);
+        AppBannerManager.setOverrideSegmentationResultForTesting(true);
         mTabbedActivityTestRule.loadUrl(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
         navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, url);
+
         waitForBadgeStatus(tab, AmbientBadgeState.SHOWING);
         waitUntilAmbientBadgePromptAppears(mTabbedActivityTestRule);
-
-        // Advance 31 more days and navigate to |url| again, no ambient badge.
-        AppBannerManager.setTimeDeltaForTesting(35);
-        mTabbedActivityTestRule.loadUrl(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
-        navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, url);
-        waitForBadgeStatus(tab, AmbientBadgeState.PENDING_ENGAGEMENT);
-        checkAmbientBadgePromptNotExist(mTabbedActivityTestRule);
     }
 }
diff --git a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 55ee585b..23514b4 100644
--- a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -45,12 +45,10 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.tab.Tab;
@@ -84,7 +82,6 @@
 /** Tests the app banners. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@DisableFeatures({ChromeFeatureList.WEB_APP_AMBIENT_BADGE_SUPRESS_FIRST_VISIT})
 public class AppBannerManagerTest {
     @Rule
     public ChromeTabbedActivityTestRule mTabbedActivityTestRule =
@@ -200,6 +197,7 @@
 
         AppBannerManager.ignoreChromeChannelForTesting();
         AppBannerManager.setTotalEngagementForTesting(10);
+        AppBannerManager.setOverrideSegmentationResultForTesting(true);
         mTestServer =
                 EmbeddedTestServer.createAndStartServer(
                         ApplicationProvider.getApplicationContext());
diff --git a/chrome/browser/banners/app_banner_manager_browsertest_base.cc b/chrome/browser/banners/app_banner_manager_browsertest_base.cc
index cee6b41d..5576197 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest_base.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest_base.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/banners/app_banner_manager_browsertest_base.h"
+
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/url_util.h"
diff --git a/chrome/browser/banners/app_banner_manager_browsertest_base.h b/chrome/browser/banners/app_banner_manager_browsertest_base.h
index 02086b8..57adc75 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest_base.h
+++ b/chrome/browser/banners/app_banner_manager_browsertest_base.h
@@ -58,7 +58,7 @@
                                         const std::string& value);
 
 #if !BUILDFLAG(IS_ANDROID)
-  web_app::OsIntegrationTestOverrideBlockingRegistration os_hooks_suppress_;
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 #endif
 };
 
diff --git a/chrome/browser/chromeos/extensions/info_private_lacros_apitest.cc b/chrome/browser/chromeos/extensions/info_private_lacros_apitest.cc
new file mode 100644
index 0000000..ff4379c
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/info_private_lacros_apitest.cc
@@ -0,0 +1,136 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_future.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h"
+#include "chromeos/lacros/lacros_service.h"
+#include "content/public/test/browser_test.h"
+
+namespace {
+
+bool IsSupportedAsh() {
+  return (chromeos::LacrosService::Get()
+              ->GetInterfaceVersion<crosapi::mojom::TestController>() >=
+          static_cast<int>(crosapi::mojom::TestController::MethodMinVersions::
+                               kEnableStatisticsProviderForTestingMinVersion));
+}
+
+}  // namespace
+
+// Tests chromeosInfoPrivate.get function for the properties provided by
+// chromeos machine statistics.
+class ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest
+    : public extensions::ExtensionApiTest {
+ public:
+  ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest() = default;
+
+  ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest(
+      const ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest&) =
+      delete;
+  ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest& operator=(
+      const ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest&) =
+      delete;
+  ~ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest() override =
+      default;
+
+  void SetUpOnMainThread() override {
+    if (!IsSupportedAsh()) {
+      GTEST_SKIP() << "Unsupported Ash version.";
+    }
+
+    auto& test_controller = chromeos::LacrosService::Get()
+                                ->GetRemote<crosapi::mojom::TestController>();
+    base::test::TestFuture<void> future;
+    test_controller->EnableStatisticsProviderForTesting(/*enable=*/true,
+                                                        future.GetCallback());
+    EXPECT_TRUE(future.Wait());
+
+    extensions::ExtensionApiTest::SetUpOnMainThread();
+  }
+
+  void TearDownOnMainThread() override {
+    if (!IsSupportedAsh()) {
+      return;
+    }
+
+    auto& test_controller = chromeos::LacrosService::Get()
+                                ->GetRemote<crosapi::mojom::TestController>();
+    base::test::TestFuture<void> future;
+    test_controller->ClearAllMachineStatistics(future.GetCallback());
+    EXPECT_TRUE(future.WaitAndClear());
+    test_controller->EnableStatisticsProviderForTesting(/*enable=*/false,
+                                                        future.GetCallback());
+    EXPECT_TRUE(future.Wait());
+
+    extensions::ExtensionApiTest::TearDownOnMainThread();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(
+    ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest,
+    TestPropertiesUnset) {
+  auto& test_controller = chromeos::LacrosService::Get()
+                              ->GetRemote<crosapi::mojom::TestController>();
+  base::test::TestFuture<void> future;
+  test_controller->ClearAllMachineStatistics(future.GetCallback());
+  EXPECT_TRUE(future.Wait());
+
+  ASSERT_TRUE(
+      RunExtensionTest("chromeos_info_private/extended",
+                       {.custom_arg = "Machine Statistics Properties - Unset",
+                        .launch_as_platform_app = true}))
+      << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(
+    ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest,
+    TestDeviceRequisitionRemora) {
+  auto& test_controller = chromeos::LacrosService::Get()
+                              ->GetRemote<crosapi::mojom::TestController>();
+  base::test::TestFuture<bool> future;
+  test_controller->SetMachineStatistic(
+      crosapi::mojom::MachineStatisticKeyType::kOemDeviceRequisitionKey,
+      "remora", future.GetCallback());
+  ASSERT_TRUE(future.Take());
+
+  ASSERT_TRUE(RunExtensionTest("chromeos_info_private/extended",
+                               {.custom_arg = "Device Requisition - Remora",
+                                .launch_as_platform_app = true}))
+      << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(
+    ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest,
+    TestHWID) {
+  auto& test_controller = chromeos::LacrosService::Get()
+                              ->GetRemote<crosapi::mojom::TestController>();
+  base::test::TestFuture<bool> future;
+  test_controller->SetMachineStatistic(
+      crosapi::mojom::MachineStatisticKeyType::kHardwareClassKey, "test_hw",
+      future.GetCallback());
+  ASSERT_TRUE(future.Take());
+
+  ASSERT_TRUE(
+      RunExtensionTest("chromeos_info_private/extended",
+                       {.custom_arg = "HWID", .launch_as_platform_app = true}))
+      << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(
+    ChromeOSInfoPrivateMachineStatisticsPropertyGetFunctionTest,
+    TestCustomizationID) {
+  auto& test_controller = chromeos::LacrosService::Get()
+                              ->GetRemote<crosapi::mojom::TestController>();
+  base::test::TestFuture<bool> future;
+  test_controller->SetMachineStatistic(
+      crosapi::mojom::MachineStatisticKeyType::kCustomizationIdKey,
+      "test_customization_id", future.GetCallback());
+  ASSERT_TRUE(future.Take());
+
+  ASSERT_TRUE(RunExtensionTest(
+      "chromeos_info_private/extended",
+      {.custom_arg = "CustomizationId", .launch_as_platform_app = true}))
+      << message_;
+}
diff --git a/chrome/browser/content_settings/DEPS b/chrome/browser/content_settings/DEPS
deleted file mode 100644
index 327971a..0000000
--- a/chrome/browser/content_settings/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+pdf/pdf_features.h",
-]
\ No newline at end of file
diff --git a/chrome/browser/dips/dips_bounce_detector.cc b/chrome/browser/dips/dips_bounce_detector.cc
index a02424bc..44704287 100644
--- a/chrome/browser/dips/dips_bounce_detector.cc
+++ b/chrome/browser/dips/dips_bounce_detector.cc
@@ -315,9 +315,8 @@
 
 void DIPSRedirectContext::HandleUncommitted(
     DIPSNavigationStart navigation_start,
-    std::vector<DIPSRedirectInfoPtr> server_redirects) {
-  // Uncommitted navigations leave the user on the last-committed page; use that
-  // for `final_url`.
+    std::vector<DIPSRedirectInfoPtr> server_redirects,
+    GURL final_url) {
   absl::visit(  //
       base::Overloaded{
           [&](DIPSRedirectInfoPtr client_redirect) {
@@ -329,8 +328,6 @@
             DIPSRedirectContext temp_context(handler_, issue_handler_,
                                              initial_url_,
                                              GetRedirectChainLength());
-            // Copy the URL of `client_redirect` before moving it.
-            GURL final_url = client_redirect->url;
             temp_context.AppendClientRedirect(std::move(client_redirect));
             temp_context.AppendServerRedirects(std::move(server_redirects));
             temp_context.ReportIssue(final_url);
@@ -346,11 +343,9 @@
                                              previous_nav_last_committed_url,
                                              /*redirect_prefix_count=*/0);
             temp_context.AppendServerRedirects(std::move(server_redirects));
-            temp_context.ReportIssue(
-                /*final_url=*/previous_nav_last_committed_url);
-            temp_context.EndChain(
-                /*final_url=*/std::move(previous_nav_last_committed_url),
-                /*current_page_has_sticky_activation=*/false);
+            temp_context.ReportIssue(final_url);
+            temp_context.EndChain(std::move(final_url),
+                                  /*current_page_has_sticky_activation=*/false);
           },
       },
       std::move(navigation_start));
@@ -970,17 +965,9 @@
         std::move(server_state->navigation_start), std::move(redirects),
         navigation_handle->GetURL(), current_page_has_sticky_activation);
   } else {
-    // For uncommitted navigations, treat the last URL visited as a server
-    // redirect, so it is considered a potential tracker.
-    const size_t i = access_types.size() - 1;
-    redirects.push_back(std::make_unique<DIPSRedirectInfo>(
-        /*url=*/navigation_handle->GetRedirectChain()[i],
-        /*redirect_type=*/DIPSRedirectType::kServer,
-        /*access_type=*/access_types[i],
-        /*source_id=*/navigation_handle->GetRedirectSourceId(i),
-        /*time=*/clock_->Now()));
     committed_redirect_context_.HandleUncommitted(
-        std::move(server_state->navigation_start), std::move(redirects));
+        std::move(server_state->navigation_start), std::move(redirects),
+        navigation_handle->GetURL());
   }
 
   if (navigation_handle->HasCommitted()) {
diff --git a/chrome/browser/dips/dips_bounce_detector.h b/chrome/browser/dips/dips_bounce_detector.h
index 4ccb8fe..ef5de14 100644
--- a/chrome/browser/dips/dips_bounce_detector.h
+++ b/chrome/browser/dips/dips_bounce_detector.h
@@ -94,7 +94,8 @@
   // navigation. It will take into account the length and initial URL of the
   // current chain (without modifying it).
   void HandleUncommitted(DIPSNavigationStart navigation_start,
-                         std::vector<DIPSRedirectInfoPtr> server_redirects);
+                         std::vector<DIPSRedirectInfoPtr> server_redirects,
+                         GURL final_url);
 
   // Either calls for termination of the in-progress redirect chain, with a
   // start of a new one, or extends it, according to the value of
diff --git a/chrome/browser/dips/dips_bounce_detector_browsertest.cc b/chrome/browser/dips/dips_bounce_detector_browsertest.cc
index 39b5e95..8796fb2f5 100644
--- a/chrome/browser/dips/dips_bounce_detector_browsertest.cc
+++ b/chrome/browser/dips/dips_bounce_detector_browsertest.cc
@@ -20,7 +20,6 @@
 #include "base/run_loop.h"
 #include "base/strings/escape.h"
 #include "base/strings/strcat.h"
-#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
@@ -71,7 +70,6 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "net/dns/mock_host_resolver.h"
-#include "net/http/http_status_code.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -173,20 +171,6 @@
   return origins;
 }
 
-// /nocontent-set-cookie
-// Returns a HTTP 204 No Content response that sets a cookie.
-std::unique_ptr<net::test_server::HttpResponse> HandleNoContentSetCookie(
-    const net::test_server::HttpRequest& request) {
-  if (request.relative_url != "/nocontent-set-cookie") {
-    return nullptr;
-  }
-
-  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
-  http_response->set_code(net::HTTP_NO_CONTENT);
-  http_response->AddCustomHeader("Set-Cookie", "no-content=true");
-  return http_response;
-}
-
 }  // namespace
 
 // Keeps a log of DidStartNavigation, OnCookiesAccessed, and DidFinishNavigation
@@ -2855,33 +2839,6 @@
       ->RemoveObserver(logger);
 }
 
-// Verifies that a HTTP 204 (No Content) response is treated like a bounce.
-IN_PROC_BROWSER_TEST_F(DIPSBounceDetectorBrowserTest, NoContentSetCookie) {
-  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
-  https_server.AddDefaultHandlers(kChromeTestDataDir);
-  https_server.RegisterRequestHandler(
-      base::BindRepeating(&HandleNoContentSetCookie));
-  ASSERT_TRUE(https_server.Start());
-  content::WebContents* web_contents = GetActiveWebContents();
-
-  GURL committed_url = https_server.GetURL("a.test", "/title1.html");
-  RedirectChainObserver observer(
-      DIPSService::Get(web_contents->GetBrowserContext()), committed_url,
-      /*expected_match_count=*/2);
-  ASSERT_TRUE(content::NavigateToURL(web_contents, committed_url));
-
-  GURL nocontent_url = https_server.GetURL("b.test", "/nocontent-set-cookie");
-  ASSERT_TRUE(
-      content::NavigateToURL(web_contents, nocontent_url, committed_url));
-  observer.Wait();
-
-  base::test::TestFuture<const std::vector<std::string>&> deleted_sites;
-  DIPSService::Get(web_contents->GetBrowserContext())
-      ->DeleteEligibleSitesImmediately(deleted_sites.GetCallback());
-  ASSERT_THAT(deleted_sites.Get(), ElementsAre("b.test"));
-}
-
 class DIPSThrottlingBrowserTest : public DIPSBounceDetectorBrowserTest {
  public:
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/dips/dips_bounce_detector_unittest.cc b/chrome/browser/dips/dips_bounce_detector_unittest.cc
index 4e6ba6a..012163c2 100644
--- a/chrome/browser/dips/dips_bounce_detector_unittest.cc
+++ b/chrome/browser/dips/dips_bounce_detector_unittest.cc
@@ -631,9 +631,8 @@
   EndPendingRedirectChain();
 
   EXPECT_THAT(redirects(), testing::ElementsAre(
-                               ("[1/3] a.test/ -> b.test/ (None) -> a.test/"),
-                               ("[2/3] a.test/ -> c.test/ (None) -> a.test/"),
-                               ("[3/3] a.test/ -> d.test/ (None) -> a.test/"),
+                               ("[1/2] a.test/ -> b.test/ (None) -> d.test/"),
+                               ("[2/2] a.test/ -> c.test/ (None) -> d.test/"),
                                ("[1/1] a.test/ -> e.test/ (None) -> f.test/")));
   EXPECT_THAT(GetRecordedBounces(),
               testing::UnorderedElementsAre(
@@ -641,8 +640,6 @@
                                   /*stateful=*/false),
                   MakeBounceTuple("http://c.test", mocked_bounce_time,
                                   /*stateful=*/false),
-                  MakeBounceTuple("http://d.test", mocked_bounce_time,
-                                  /*stateful=*/false),
                   MakeBounceTuple("http://e.test", mocked_bounce_time,
                                   /*stateful=*/false)));
   EXPECT_EQ(stateful_bounce_count(), 0);
@@ -665,9 +662,8 @@
   EndPendingRedirectChain();
 
   EXPECT_THAT(redirects(), testing::ElementsAre(
-                               ("[1/3] a.test/ -> b.test/ (None) -> b.test/"),
-                               ("[2/3] a.test/ -> c.test/ (None) -> b.test/"),
-                               ("[3/3] a.test/ -> d.test/ (None) -> b.test/"),
+                               ("[1/2] a.test/ -> b.test/ (None) -> d.test/"),
+                               ("[2/2] a.test/ -> c.test/ (None) -> d.test/"),
                                ("[1/2] a.test/ -> b.test/ (None) -> f.test/"),
                                ("[2/2] a.test/ -> e.test/ (None) -> f.test/")));
   EXPECT_THAT(GetRecordedBounces(),
@@ -676,8 +672,6 @@
                                   /*stateful=*/false),
                   MakeBounceTuple("http://c.test", mocked_bounce_time,
                                   /*stateful=*/false),
-                  MakeBounceTuple("http://d.test", mocked_bounce_time,
-                                  /*stateful=*/false),
                   MakeBounceTuple("http://e.test", mocked_bounce_time,
                                   /*stateful=*/false)));
   EXPECT_EQ(stateful_bounce_count(), 0);
@@ -728,8 +722,7 @@
       .RedirectTo("http://d.test")
       .AccessCookie(CookieOperation::kChange)
       .Finish(false);
-  EXPECT_THAT(GetReportedSites(),
-              testing::ElementsAre("b.test, c.test, d.test"));
+  EXPECT_THAT(GetReportedSites(), testing::ElementsAre("b.test, c.test"));
 
   // Because the previous navigation didn't commit, the following chain still
   // starts from http://a.test/.
@@ -739,7 +732,7 @@
       .AccessCookie(CookieOperation::kChange)
       .Finish(true);
   EXPECT_THAT(GetReportedSites(),
-              testing::ElementsAre("b.test, c.test, d.test", "e.test"));
+              testing::ElementsAre("b.test, c.test", "e.test"));
 }
 
 TEST_F(DIPSBounceDetectorTest,
@@ -862,14 +855,13 @@
       .RedirectTo("http://c.test")
       .AccessCookie(CookieOperation::kChange)
       .Finish(false);
-  EXPECT_THAT(GetReportedSites(), testing::ElementsAre("b.test, c.test"));
+  EXPECT_THAT(GetReportedSites(), testing::ElementsAre("b.test"));
 
   // Navigate without a click (i.e. by C-redirecting) to d.test.
   // NOTE: Because the previous navigation didn't commit, the chain still
   // starts from http://a.test/.
   NavigateTo("http://d.test", kNoUserGesture);
-  EXPECT_THAT(GetReportedSites(),
-              testing::ElementsAre("b.test, c.test", "a.test"));
+  EXPECT_THAT(GetReportedSites(), testing::ElementsAre("b.test", "a.test"));
 }
 
 const std::vector<std::string>& GetAllRedirectMetrics() {
@@ -1289,7 +1281,8 @@
   ASSERT_EQ(chains.size(), 0u);
   context.HandleUncommitted(
       GURL("http://d.test/"),
-      MakeServerRedirects({"http://e.test/", "http://f.test/"}));
+      MakeServerRedirects({"http://e.test/", "http://f.test/"}),
+      GURL("http://g.test/"));
   ASSERT_EQ(chains.size(), 1u);
   context.AppendCommitted(GURL("http://h.test/"),
                           MakeServerRedirects({"http://i.test/"}),
@@ -1301,7 +1294,7 @@
   // First, the uncommitted (middle) chain.
   EXPECT_THAT(chains[0].first,
               AllOf(HasInitialUrl("http://d.test/"),
-                    HasFinalUrl("http://d.test/"), HasLength(2u)));
+                    HasFinalUrl("http://g.test/"), HasLength(2u)));
   EXPECT_THAT(chains[0].second,
               ElementsAre(HasUrl("http://e.test/"), HasUrl("http://f.test/")));
   // Then the initially-started chain.
@@ -1332,7 +1325,8 @@
   // Uncommitted navigation:
   context.HandleUncommitted(
       MakeClientRedirect("http://d.test/"),
-      MakeServerRedirects({"http://e.test/", "http://f.test/"}));
+      MakeServerRedirects({"http://e.test/", "http://f.test/"}),
+      GURL("http://g.test/"));
   ASSERT_EQ(chains.size(), 1u);
   context.AppendCommitted(MakeClientRedirect("http://h.test/"),
                           MakeServerRedirects({"http://i.test/"}),
@@ -1346,7 +1340,7 @@
   // plus the uncommitted part (3 redirects, starting from d.test).
   EXPECT_THAT(chains[0].first,
               AllOf(HasInitialUrl("http://a.test/"),
-                    HasFinalUrl("http://d.test/"), HasLength(5u)));
+                    HasFinalUrl("http://g.test/"), HasLength(5u)));
   // But only the 3 uncommitted redirects are included in the vector.
   EXPECT_THAT(chains[0].second,
               ElementsAre(HasUrl("http://d.test/"), HasUrl("http://e.test/"),
@@ -1377,18 +1371,26 @@
                           false);
   ASSERT_EQ(chains.size(), 1u);
 
-  context.EndChain(GURL("http://e.test/"), false);
+  context.HandleUncommitted(GURL("http://c.test/"), {}, GURL("http://d.test/"));
   ASSERT_EQ(chains.size(), 2u);
 
+  context.EndChain(GURL("http://e.test/"), false);
+  ASSERT_EQ(chains.size(), 3u);
+
   EXPECT_THAT(chains[0].first,
               AllOf(HasInitialUrl("http://a.test/"),
                     HasFinalUrl("http://b.test/"), HasLength(0u)));
   EXPECT_THAT(chains[0].second, IsEmpty());
 
   EXPECT_THAT(chains[1].first,
+              AllOf(HasInitialUrl("http://c.test/"),
+                    HasFinalUrl("http://d.test/"), HasLength(0u)));
+  EXPECT_THAT(chains[1].second, IsEmpty());
+
+  EXPECT_THAT(chains[2].first,
               AllOf(HasInitialUrl("http://b.test/"),
                     HasFinalUrl("http://e.test/"), HasLength(0u)));
-  EXPECT_THAT(chains[1].second, IsEmpty());
+  EXPECT_THAT(chains[2].second, IsEmpty());
 }
 
 TEST(DIPSRedirectContextTest, AddLateCookieAccess) {
diff --git a/chrome/browser/dips/dips_test_utils.cc b/chrome/browser/dips/dips_test_utils.cc
index 2f8d5b1..fd81eae 100644
--- a/chrome/browser/dips/dips_test_utils.cc
+++ b/chrome/browser/dips/dips_test_utils.cc
@@ -164,10 +164,8 @@
 }
 
 RedirectChainObserver::RedirectChainObserver(DIPSService* service,
-                                             GURL final_url,
-                                             size_t expected_match_count)
-    : final_url_(std::move(final_url)),
-      expected_match_count_(expected_match_count) {
+                                             GURL final_url)
+    : final_url_(std::move(final_url)) {
   obs_.Observe(service);
 }
 
@@ -176,8 +174,7 @@
 void RedirectChainObserver::OnChainHandled(
     const DIPSRedirectChainInfoPtr& chain) {
   handle_call_count++;
-  if (chain->final_url == final_url_ &&
-      ++match_count_ == expected_match_count_) {
+  if (chain->final_url == final_url_) {
     run_loop_.Quit();
   }
 }
diff --git a/chrome/browser/dips/dips_test_utils.h b/chrome/browser/dips/dips_test_utils.h
index f8bd9075..070eb64 100644
--- a/chrome/browser/dips/dips_test_utils.h
+++ b/chrome/browser/dips/dips_test_utils.h
@@ -180,9 +180,7 @@
 
 class RedirectChainObserver : public DIPSService::Observer {
  public:
-  explicit RedirectChainObserver(DIPSService* service,
-                                 GURL final_url,
-                                 size_t expected_match_count = 1);
+  explicit RedirectChainObserver(DIPSService* service, GURL final_url);
   ~RedirectChainObserver() override;
 
   void OnChainHandled(const DIPSRedirectChainInfoPtr& chain) override;
@@ -193,8 +191,6 @@
 
  private:
   GURL final_url_;
-  size_t match_count_ = 0;
-  size_t expected_match_count_;
   base::RunLoop run_loop_;
   base::ScopedObservation<DIPSService, Observer> obs_{this};
 };
diff --git a/chrome/browser/download/DEPS b/chrome/browser/download/DEPS
deleted file mode 100644
index 487e1f81..0000000
--- a/chrome/browser/download/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+pdf/pdf_features.h"
-]
\ No newline at end of file
diff --git a/chrome/browser/download/download_browsertest_utils.cc b/chrome/browser/download/download_browsertest_utils.cc
index 048a6cff..338624a 100644
--- a/chrome/browser/download/download_browsertest_utils.cc
+++ b/chrome/browser/download/download_browsertest_utils.cc
@@ -3,13 +3,12 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/download/download_browsertest_utils.h"
-#include "base/memory/raw_ptr.h"
-#include "base/strings/utf_string_conversions.h"
-#include "content/public/test/browser_test_utils.h"
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/memory/raw_ptr.h"
 #include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/test/test_file_util.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/profiles/profile.h"
@@ -21,6 +20,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/download_request_utils.h"
+#include "content/public/test/browser_test_utils.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "third_party/blink/public/common/switches.h"
diff --git a/chrome/browser/download/download_browsertest_utils.h b/chrome/browser/download/download_browsertest_utils.h
index 4a897e86..7e9e0f51 100644
--- a/chrome/browser/download/download_browsertest_utils.h
+++ b/chrome/browser/download/download_browsertest_utils.h
@@ -11,6 +11,7 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/download/download_test_file_activity_observer.h"
 #include "chrome/browser/extensions/install_verifier.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/slow_download_http_response.h"
@@ -269,6 +270,8 @@
   }
 
  private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
+
   // Location of the test data.
   base::FilePath test_dir_;
 
diff --git a/chrome/browser/extensions/DEPS b/chrome/browser/extensions/DEPS
index 18f2f471..99e9489 100644
--- a/chrome/browser/extensions/DEPS
+++ b/chrome/browser/extensions/DEPS
@@ -4,7 +4,6 @@
   "+components/guest_view/common",
   "+components/live_caption",
   "+extensions/strings/grit/extensions_strings.h",
-  "+pdf/pdf_features.h",
   "+services/network/public",
 
   # For access to testing command line switches.
diff --git a/chrome/browser/extensions/api/DEPS b/chrome/browser/extensions/api/DEPS
index 3fafb25..aa705a3 100644
--- a/chrome/browser/extensions/api/DEPS
+++ b/chrome/browser/extensions/api/DEPS
@@ -3,7 +3,6 @@
   "+components/live_caption",
   "+services/device/public",
   "+components/device_reauth",
-  "+pdf/pdf_features.h",
    # Enable remote assistance on Chrome OS
   "+remoting/host",
 ]
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
index 83abbed7..06da262 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_util.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
@@ -313,7 +313,8 @@
   card.image_src =
       card_art_image ? webui::GetBitmapDataUrl(card_art_image->AsBitmap())
                      : CardNetworkToIconResourceIdString(credit_card.network());
-  if (credit_card.IsCardEligibleForBenefits() &&
+  if (personal_data.payments_data_manager().IsCardEligibleForBenefits(
+          credit_card) &&
       credit_card.product_terms_url().is_valid()) {
     card.product_terms_url = credit_card.product_terms_url().spec();
   }
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index 91b6551..2ebd67ff 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/web_applications/extension_status_utils.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_ui_manager.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -142,8 +143,7 @@
 
  protected:
   base::AutoReset<bool> enable_chrome_apps_;
-  // Stop test from installing OS hooks.
-  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 INSTANTIATE_TEST_SUITE_P(PersistentBackground,
@@ -207,7 +207,7 @@
  protected:
   static const char kManifest[];
   static const char kAppManifest[];
-  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
+
   void SetUpOnMainThread() override {
     ExtensionManagementApiTest::SetUpOnMainThread();
     https_test_server_.ServeFilesFromDirectory(test_data_dir_);
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 7b0c25d..6ec196e 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -787,6 +787,10 @@
   (*s_allowlist)[::ash::prefs::kIsolatedWebAppsEnabled] =
       settings_api::PrefType::kBoolean;
 
+  // App - On-Device Parental Controls
+  (*s_allowlist)[::ash::prefs::kOnDeviceAppControlsSetupCompleted] =
+      settings_api::PrefType::kBoolean;
+
   // Ambient Mode.
   (*s_allowlist)[ash::ambient::prefs::kAmbientModeEnabled] =
       settings_api::PrefType::kBoolean;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 53545e6..481449ba 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4826,11 +4826,6 @@
     "expiry_milestone": 125
   },
   {
-    "name": "history-journeys-labels",
-    "owners": [ "tommycli@chromium.org", "chrome-journeys@google.com" ],
-    "expiry_milestone": 125
-  },
-  {
     "name": "history-journeys-show-all-clusters",
     "owners": [ "sophiechang@chromium.org", "chrome-journeys@google.com", "chrome-intelligence-core@google.com" ],
     "expiry_milestone": 125
@@ -5653,13 +5648,6 @@
     "expiry_milestone": 114
   },
   {
-    "name": "ml-mobile-pwa-prompt",
-    "owners": [
-      "eirage@chromium.org", "mwi-team-core@google.com"
-    ],
-    "expiry_milestone": 120
-  },
-  {
     "name": "modern-tab-strip",
     "owners": [ "bling-flags@google.com" ],
     "expiry_milestone": 125
@@ -7287,7 +7275,7 @@
   {
     "name": "request-desktop-site-window-setting",
     "owners": [ "shuyng@google.com", "skavuluru@google.com", "clank-app-team@google.com" ],
-    "expiry_milestone": 126
+    "expiry_milestone": 130
   },
   {
     "name": "reset-shortcut-customizations",
@@ -7671,7 +7659,7 @@
   {
     "name": "site-instance-groups-for-data-urls",
     "owners": [ "yangsharon@chromium.org", "site-isolation-dev@chromium.org" ],
-    "expiry_milestone": 125
+    "expiry_milestone": 140
   },
   {
     "_comment1": "Shipping some form of Site Isolation to Android is tracked",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index a4c8048..f85b326 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2175,10 +2175,6 @@
 const char kJourneysName[] = "History Journeys";
 const char kJourneysDescription[] = "Enables the History Journeys UI.";
 
-const char kJourneysLabelsName[] = "History Journeys Labels";
-const char kJourneysLabelsDescription[] =
-    "Enables labels for Journeys within the History Journeys UI.";
-
 const char kJourneysShowAllClustersName[] =
     "History Journeys Show All Clusters";
 const char kJourneysShowAllClustersDescription[] =
@@ -4173,12 +4169,6 @@
 const char kMessagesForAndroidStackingAnimationDescription[] =
     "When enabled, Messages UI will use the new stacking animation.";
 
-const char kMobilePWAInstallPromptMlName[] =
-    "Use ML to show mobile PWA install prompt";
-const char kMobilePWAInstallPromptMlDescription[] =
-    "When enabled, will use ML result to decide whether mobile PWA install "
-    "prompt should be shown.";
-
 const char kMouseAndTrackpadDropdownMenuName[] =
     "Android Mouse & Trackpad Drop-down Text Selection Menu";
 const char kMouseAndTrackpadDropdownMenuDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 6c05e84..8df54ba7 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1246,9 +1246,6 @@
 extern const char kJourneysName[];
 extern const char kJourneysDescription[];
 
-extern const char kJourneysLabelsName[];
-extern const char kJourneysLabelsDescription[];
-
 extern const char kJourneysShowAllClustersName[];
 extern const char kJourneysShowAllClustersDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 946dedd..0529325 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -178,7 +178,6 @@
     &kCacheActivityTaskID,
     &kCastDeviceFilter,
     &kClearOmniboxFocusAfterNavigation,
-    &kCreateNewTabInitializeRenderer,
     &kCCTClientDataHeader,
     &kCCTEmbedderSpecialBehaviorTrigger,
     &kCCTExtendTrustedCdnPublisher,
@@ -364,7 +363,6 @@
     &syncer::kSyncShowIdentityErrorsForSignedInUsers,
     &syncer::kWebApkBackupAndRestoreBackend,
     &tab_groups::kTabGroupSyncAndroid,
-    &webapps::features::kAmbientBadgeSuppressFirstVisit,
     &webapps::features::kPwaUniversalInstallUi,
     &webapps::features::kWebApkInstallFailureNotification,
     &network::features::kPrivateStateTokens,
@@ -426,7 +424,7 @@
              "AndroidHatsRefactor",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kAndroidHub, "AndroidHub", base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kAndroidHub, "AndroidHub", base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kAndroidImprovedBookmarks,
              "AndroidImprovedBookmarks",
@@ -493,10 +491,6 @@
              "ClearOmniboxFocusAfterNavigation",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kCreateNewTabInitializeRenderer,
-             "CreateNewTabInitializeRenderer",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kCCTClientDataHeader,
              "CCTClientDataHeader",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -837,7 +831,7 @@
 
 BASE_FEATURE(kTabAndLinkDragDropAndroid,
              "TabAndLinkDragDropAndroid",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kTabGroupPaneAndroid,
              "TabGroupPaneAndroid",
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index ab4f5ab..68ab7b9 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -44,7 +44,6 @@
 BASE_DECLARE_FEATURE(kBrowserControlsEarlyResize);
 BASE_DECLARE_FEATURE(kCacheActivityTaskID);
 BASE_DECLARE_FEATURE(kClearOmniboxFocusAfterNavigation);
-BASE_DECLARE_FEATURE(kCreateNewTabInitializeRenderer);
 BASE_DECLARE_FEATURE(kCastDeviceFilter);
 BASE_DECLARE_FEATURE(kCCTClientDataHeader);
 BASE_DECLARE_FEATURE(kCCTEmbedderSpecialBehaviorTrigger);
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 a28a6e21..d42f001 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
@@ -258,8 +258,6 @@
     public static final String CONTEXT_MENU_TRANSLATE_WITH_GOOGLE_LENS =
             "ContextMenuTranslateWithGoogleLens";
     public static final String CORMORANT = "Cormorant";
-    public static final String CREATE_NEW_TAB_INITIALIZE_RENDERER =
-            "CreateNewTabInitializeRenderer";
     public static final String DARKEN_WEBSITES_CHECKBOX_IN_THEMES_SETTING =
             "DarkenWebsitesCheckboxInThemesSetting";
     public static final String DATA_SHARING = "DataSharing";
@@ -502,8 +500,6 @@
     public static final String VOICE_SEARCH_AUDIO_CAPTURE_POLICY = "VoiceSearchAudioCapturePolicy";
     public static final String WEB_APK_ALLOW_ICON_UPDATE = "WebApkAllowIconUpdate";
     public static final String WEB_APK_BACKUP_AND_RESTORE_BACKEND = "WebApkBackupAndRestoreBackend";
-    public static final String WEB_APP_AMBIENT_BADGE_SUPRESS_FIRST_VISIT =
-            "AmbientBadgeSuppressFirstVisit";
     public static final String WEB_APK_INSTALL_FAILURE_NOTIFICATION =
             "WebApkInstallFailureNotification";
     public static final String WEB_AUTHN_ENABLE_CABLE_AUTHENTICATOR =
@@ -521,7 +517,7 @@
             newCachedFlag(ANDROID_APP_INTEGRATION, false);
     public static final CachedFlag sAndroidElegantTextHeight =
             newCachedFlag(ANDROID_ELEGANT_TEXT_HEIGHT, false);
-    public static final CachedFlag sAndroidHub = newCachedFlag(ANDROID_HUB, false);
+    public static final CachedFlag sAndroidHub = newCachedFlag(ANDROID_HUB, true);
     public static final CachedFlag sAndroidTabGroupStableIds =
             newCachedFlag(ANDROID_TAB_GROUP_STABLE_IDS, false);
     public static final CachedFlag sAppSpecificHistory = newCachedFlag(APP_SPECIFIC_HISTORY, false);
@@ -639,7 +635,7 @@
     public static final CachedFlag sTabGroupSyncAndroid =
             newCachedFlag(TAB_GROUP_SYNC_ANDROID, false);
     public static final CachedFlag sTabLinkDragDropAndroid =
-            newCachedFlag(TAB_LINK_DRAG_DROP_ANDROID, false);
+            newCachedFlag(TAB_LINK_DRAG_DROP_ANDROID, true);
     public static final CachedFlag sTabResumptionModuleAndroid =
             newCachedFlag(TAB_RESUMPTION_MODULE_ANDROID, false);
     public static final CachedFlag sTabStateFlatBuffer = newCachedFlag(TAB_STATE_FLATBUFFER, false);
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index d52aaac..ed7f2f4 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -235,6 +235,12 @@
     LAZY_INSTANCE_INITIALIZER;
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_WIN)
+// Needs to be kept in sync with the writer in PlatformExperienceHelper.
+const char kPlatformExperienceHelperHistogramAllocatorName[] =
+    "PlatformExperienceHelperMetrics";
+#endif  // BUILDFLAG(IS_WIN)
+
 void RegisterFileMetricsPreferences(PrefRegistrySimple* registry) {
   metrics::FileMetricsProvider::RegisterSourcePrefs(registry,
                                                     kBrowserMetricsName);
@@ -251,6 +257,8 @@
 
   metrics::FileMetricsProvider::RegisterSourcePrefs(
       registry, notification_helper::kNotificationHelperHistogramAllocatorName);
+  metrics::FileMetricsProvider::RegisterSourcePrefs(
+      registry, kPlatformExperienceHelperHistogramAllocatorName);
 #endif
 }
 
@@ -362,13 +370,16 @@
       metrics::FileMetricsProvider::ASSOCIATE_CURRENT_RUN,
       installer::kSetupHistogramAllocatorName));
 
-  // When metrics reporting is enabled, register the notification_helper metrics
-  // files; otherwise delete any existing files in order to preserve user
-  // privacy.
+  // When metrics reporting is enabled, register the notification_helper
+  // and platform experience helper metrics files; otherwise delete any
+  // existing files in order to preserve user privacy.
   if (!user_data_dir.empty()) {
     base::FilePath notification_helper_metrics_upload_dir =
         user_data_dir.AppendASCII(
             notification_helper::kNotificationHelperHistogramAllocatorName);
+    base::FilePath platform_experience_helper_metrics_upload_dir =
+        user_data_dir.AppendASCII(
+            kPlatformExperienceHelperHistogramAllocatorName);
 
     if (metrics_reporting_enabled) {
       file_metrics_provider->RegisterSource(
@@ -377,6 +388,12 @@
               metrics::FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
               metrics::FileMetricsProvider::ASSOCIATE_CURRENT_RUN,
               notification_helper::kNotificationHelperHistogramAllocatorName));
+      file_metrics_provider->RegisterSource(
+          metrics::FileMetricsProvider::Params(
+              platform_experience_helper_metrics_upload_dir,
+              metrics::FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
+              metrics::FileMetricsProvider::ASSOCIATE_CURRENT_RUN,
+              kPlatformExperienceHelperHistogramAllocatorName));
     } else {
       base::ThreadPool::PostTask(
           FROM_HERE,
@@ -384,6 +401,12 @@
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
           base::GetDeletePathRecursivelyCallback(
               std::move(notification_helper_metrics_upload_dir)));
+      base::ThreadPool::PostTask(
+          FROM_HERE,
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+          base::GetDeletePathRecursivelyCallback(
+              std::move(platform_experience_helper_metrics_upload_dir)));
     }
   }
 #endif
diff --git a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
index c0be92ca..50dfd16c 100644
--- a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
+++ b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/common/chrome_features.h"
@@ -1072,6 +1073,7 @@
   }
 
  private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
diff --git a/chrome/browser/pdf/DEPS b/chrome/browser/pdf/DEPS
index 502b0d5..b320e01 100644
--- a/chrome/browser/pdf/DEPS
+++ b/chrome/browser/pdf/DEPS
@@ -1,6 +1,5 @@
 include_rules = [
   "+chrome/browser/profiles/profile.h",
-  "+pdf/pdf_features.h",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/plugins/DEPS b/chrome/browser/plugins/DEPS
deleted file mode 100644
index cc864aa..0000000
--- a/chrome/browser/plugins/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+pdf/pdf_features.h",
-]
diff --git a/chrome/browser/printing/DEPS b/chrome/browser/printing/DEPS
index 28c8ff5d..1f4600f 100644
--- a/chrome/browser/printing/DEPS
+++ b/chrome/browser/printing/DEPS
@@ -1,6 +1,5 @@
 include_rules = [
   "+pdf/pdf.h",  # Test only.
-  "+pdf/pdf_features.h",  # Test only
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/privacy_sandbox/tracking_protection_settings_browsertest.cc b/chrome/browser/privacy_sandbox/tracking_protection_settings_browsertest.cc
index 3d5a667..a487198 100644
--- a/chrome/browser/privacy_sandbox/tracking_protection_settings_browsertest.cc
+++ b/chrome/browser/privacy_sandbox/tracking_protection_settings_browsertest.cc
@@ -20,14 +20,8 @@
 
 class TrackingProtectionSettingsMetricsBrowserTest
     : public InProcessBrowserTest {
- public:
-  TrackingProtectionSettingsMetricsBrowserTest() {
-    feature_list_.InitAndEnableFeature(privacy_sandbox::kIpProtectionV1);
-  }
-
  protected:
   base::HistogramTester histogram_tester_;
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(TrackingProtectionSettingsMetricsBrowserTest,
@@ -36,6 +30,8 @@
                                        false, 1);
   histogram_tester_.ExpectUniqueSample("Settings.IpProtection.Enabled", false,
                                        1);
+  histogram_tester_.ExpectUniqueSample(
+      "Settings.FingerprintingProtection.Enabled", false, 1);
 }
 
 class TrackingProtectionSettingsForEnterpriseBrowserTest
diff --git a/chrome/browser/privacy_sandbox/tracking_protection_settings_factory.cc b/chrome/browser/privacy_sandbox/tracking_protection_settings_factory.cc
index 7caf2104..f5b2d80 100644
--- a/chrome/browser/privacy_sandbox/tracking_protection_settings_factory.cc
+++ b/chrome/browser/privacy_sandbox/tracking_protection_settings_factory.cc
@@ -55,11 +55,12 @@
     } else {
       base::UmaHistogramBoolean("Settings.TrackingProtection.Enabled", false);
     }
-    if (base::FeatureList::IsEnabled(privacy_sandbox::kIpProtectionV1)) {
-      base::UmaHistogramBoolean(
-          "Settings.IpProtection.Enabled",
-          profile->GetPrefs()->GetBoolean(prefs::kIpProtectionEnabled));
-    }
+    base::UmaHistogramBoolean(
+        "Settings.IpProtection.Enabled",
+        profile->GetPrefs()->GetBoolean(prefs::kIpProtectionEnabled));
+    base::UmaHistogramBoolean("Settings.FingerprintingProtection.Enabled",
+                              profile->GetPrefs()->GetBoolean(
+                                  prefs::kFingerprintingProtectionEnabled));
   }
 
   return std::make_unique<privacy_sandbox::TrackingProtectionSettings>(
diff --git a/chrome/browser/renderer_context_menu/DEPS b/chrome/browser/renderer_context_menu/DEPS
deleted file mode 100644
index 487e1f81..0000000
--- a/chrome/browser/renderer_context_menu/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+pdf/pdf_features.h"
-]
\ No newline at end of file
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index 9790051..50fb748 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -58,6 +58,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
@@ -428,7 +429,7 @@
   }
 
  private:
-  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::test::ScopedFeatureList scoped_feature_list_;
   AllowPreCommitInputFlagMixin allow_pre_commit_input_flag_mixin_{mixin_host_};
 };
diff --git a/chrome/browser/resources/ash/settings/common/types.ts b/chrome/browser/resources/ash/settings/common/types.ts
index 46ee940..13176a0 100644
--- a/chrome/browser/resources/ash/settings/common/types.ts
+++ b/chrome/browser/resources/ash/settings/common/types.ts
@@ -11,3 +11,6 @@
 export interface PrefsState {
   [key: string]: any;
 }
+
+export type UserActionSettingPrefChangeEvent =
+    CustomEvent<{prefKey: string, value: any}>;
diff --git a/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.html b/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.html
index 2132ed0..1ac1a1f 100644
--- a/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.html
+++ b/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.html
@@ -321,6 +321,14 @@
           warning$="[[showRestrictedConnectivity_(managedProperties_,
               deviceState_)]]"
           disabled="[[disabled_]]">
+        <template is="dom-if" if="[[isApnManaged_(globalPolicy)]]">
+          <!-- TODO(b/335486874): Fix tooltip in RTL. -->
+          <cr-tooltip-icon id="apnManagedIcon"
+              tooltip-text="$i18n{controlledSettingPolicy}"
+              icon-class="cr20:domain"
+              tooltip-position="left">
+          </cr-tooltip-icon>
+        </template>
       </cr-link-row>
     </template>
   </template>
diff --git a/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.ts b/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.ts
index db62a93..c212fe7 100644
--- a/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.ts
+++ b/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.ts
@@ -8,6 +8,7 @@
  * for a network.
  */
 
+import 'chrome://resources/ash/common/cr_elements/policy/cr_tooltip_icon.js';
 import 'chrome://resources/ash/common/network/cr_policy_network_indicator_mojo.js';
 import 'chrome://resources/ash/common/network/network_apnlist.js';
 import 'chrome://resources/ash/common/network/network_choose_mobile.js';
@@ -321,6 +322,14 @@
         },
       },
 
+      isApnPoliciesEnabled_: {
+        type: Boolean,
+        value() {
+          return loadTimeData.valueExists('isApnPoliciesEnabled') &&
+              loadTimeData.getBoolean('isApnPoliciesEnabled');
+        },
+      },
+
       passpointSubscription_: {
         type: Object,
         notify: true,
@@ -408,6 +417,7 @@
   private suppressTextMessagesOverride_: boolean;
   private isCellularCarrierLockEnabled_: boolean;
   private isPasspointSettingsEnabled_: boolean;
+  private isApnPoliciesEnabled_: boolean;
   private isRevampWayfindingEnabled_: boolean;
   private isSecondaryUser_: boolean;
   private isTrafficCountersEnabled_: boolean;
@@ -1273,6 +1283,16 @@
         this.isCellular_(this.managedProperties_);
   }
 
+  private isApnManaged_(globalPolicy: GlobalPolicy|undefined): boolean {
+    if (!this.isApnPoliciesEnabled_) {
+      return false;
+    }
+    if (!globalPolicy) {
+      return false;
+    }
+    return !globalPolicy.allowApnModification;
+  }
+
   private shouldShowApnList_(): boolean {
     return !this.isApnRevampEnabled_ &&
         this.isCellular_(this.managedProperties_);
diff --git a/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.html b/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.html
index ce62a982..8b0c409 100644
--- a/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.html
+++ b/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.html
@@ -1,10 +1,12 @@
 <style include="settings-shared">
-  :host-context(body.revamp-wayfinding-enabled) #androidAppsRowIcon {
+  :host-context(body.revamp-wayfinding-enabled) #androidAppsRowIcon,
+  :host-context(body.revamp-wayfinding-enabled) #parentalControlsRowIcon {
     margin-inline-end: 16px;
     --iron-icon-fill-color: var(--cros-sys-primary);
   }
 
-  #androidApps[actionable]:hover {
+  #androidApps[actionable]:hover,
+  #appParentalControls[actionable]:hover {
     background-color: var(--cr-hover-background-color);
     cursor: pointer;
   }
@@ -30,13 +32,41 @@
         </cr-link-row>
       </template>
       <template is="dom-if" if="[[isAppParentalControlsFeatureAvailable_]]">
-        <cr-link-row id="appParentalControlsRow" class="settings-box"
-            start-icon="[[rowIcons_.manageApps]]"
-            label="$i18n{appParentalControlsTitle}"
-            on-click="onClickParentalControls_"
-            role-description="$i18n{subpageArrowRoleDescription}"
-            sub-label="$i18n{appParentalControlsSubtitle}">
-        </cr-link-row>
+        <div id="appParentalControls"
+            class="settings-box"
+            actionable$="[[prefs.on_device_app_controls.setup_completed.value]]"
+            on-click="onClickParentalControls_">
+          <iron-icon id="parentalControlsRowIcon"
+              icon="[[rowIcons_.manageApps]]">
+          </iron-icon>
+          <div class="start settings-box-text">
+            $i18n{appParentalControlsTitle}
+            <div class="secondary">$i18n{appParentalControlsSubtitle}</div>
+          </div>
+          <template is="dom-if"
+              if="[[!prefs.on_device_app_controls.setup_completed.value]]">
+            <div class="separator"></div>
+            <cr-button
+                on-click="setUpParentalControls_"
+                aria-label="$i18n{appParentalControlsTitle}"
+                aria-roledescription="$i18n{appParentalControlsSetUpButton}">
+              $i18n{appParentalControlsSetUpButton}
+            </cr-button>
+          </template>
+          <template is="dom-if"
+              if="[[prefs.on_device_app_controls.setup_completed.value]]">
+            <cr-icon-button class="subpage-arrow"
+                aria-label="$i18n{appParentalControlsTitle}"
+                aria-describedby="secondaryText"
+                aria-roledescription="$i18n{subpageArrowRoleDescription}">
+            </cr-icon-button>
+            <div class="separator"></div>
+            <cr-toggle id="toggle"
+                checked="[[prefs.on_device_app_controls.setup_completed.value]]"
+                on-change="disableParentalControls_">
+            </cr-toggle>
+          </template>
+        </div>
       </template>
       <template is="dom-if" if="[[showAndroidApps_]]">
         <template is="dom-if" if="[[isPlayStoreAvailable_]]" restamp>
@@ -68,7 +98,7 @@
             </template>
             <template is="dom-if" if="[[!androidAppsInfo.playStoreEnabled]]">
               <div class="separator"></div>
-              <cr-button id="enable"
+              <cr-button id="arcEnable"
                   disabled="[[isEnforced_(prefs.arc.enabled)]]"
                   on-click="onEnableAndroidAppsClick_"
                   aria-label="$i18n{androidAppsPageTitle}"
diff --git a/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.ts b/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.ts
index bf596dd..e0d3583d 100644
--- a/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.ts
+++ b/chrome/browser/resources/ash/settings/os_apps_page/os_apps_page.ts
@@ -327,7 +327,21 @@
   }
 
   private onClickParentalControls_(): void {
-    Router.getInstance().navigateTo(routes.APP_PARENTAL_CONTROLS);
+    const isParentalControlsSetup =
+        this.getPref('on_device_app_controls.setup_completed').value;
+    if (isParentalControlsSetup) {
+      Router.getInstance().navigateTo(routes.APP_PARENTAL_CONTROLS);
+    }
+  }
+
+  private setUpParentalControls_(e: Event): void {
+    this.setPrefValue('on_device_app_controls.setup_completed', true);
+    // Stop propagation to keep the subpage from opening.
+    e.stopPropagation();
+  }
+
+  private disableParentalControls_(): void {
+    this.setPrefValue('on_device_app_controls.setup_completed', false);
   }
 
   private onClickManageIsolatedWebApps_(): void {
diff --git a/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.ts b/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.ts
index 3d0fdb86..abbe927 100644
--- a/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.ts
+++ b/chrome/browser/resources/ash/settings/os_settings_ui/os_settings_ui.ts
@@ -36,6 +36,7 @@
 import {setGlobalScrollTarget} from '../common/global_scroll_target_mixin.js';
 import {isRevampWayfindingEnabled} from '../common/load_time_booleans.js';
 import {RouteObserverMixin} from '../common/route_observer_mixin.js';
+import type {UserActionSettingPrefChangeEvent} from '../common/types.js';
 import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSettingChange, recordSettingChangeForUnmappedPref} from '../metrics_recorder.js';
 import {convertPrefToSettingMetric} from '../metrics_utils.js';
 import {createPageAvailability, OsPageAvailability} from '../os_page_availability.js';
@@ -58,6 +59,7 @@
     'scroll-to-top': CustomEvent<{top: number, callback: () => void}>;
     'user-action-setting-change':
         CustomEvent<{prefKey: string, prefValue: any}>;
+    'user-action-setting-pref-change': UserActionSettingPrefChangeEvent;
   }
 }
 
@@ -229,7 +231,10 @@
     });
 
     this.addEventListener('refresh-pref', this.onRefreshPref_);
-    this.addEventListener('user-action-setting-change', this.onSettingChange_);
+
+    this.addEventListener('user-action-setting-pref-change', this.syncPrefChange_.bind(this));
+
+    this.addEventListener('user-action-setting-change', this.recordChangedSetting_.bind(this));
 
     this.addEventListener(
         'search-changed',
@@ -395,7 +400,12 @@
     this.$.prefs.refresh(e.detail);
   }
 
-  private onSettingChange_(e: CustomEvent<{prefKey: string, prefValue: any}>):
+  /**
+   * Callback for the `user-action-setting-change` event which is emitted by
+   * the `settings-prefs` singleton after a pref-based setting is updated via
+   * some user action. Records the changed setting to relevant metrics.
+   */
+  private recordChangedSetting_(e: CustomEvent<{prefKey: string, prefValue: any}>):
       void {
     const {prefKey, prefValue} = e.detail;
     const settingMetric = convertPrefToSettingMetric(prefKey, prefValue);
@@ -410,6 +420,17 @@
   }
 
   /**
+   * Callback for the `user-action-setting-pref-change` event which is emitted
+   * by settings pref control components when the prefs state should be synced
+   * after some user action (e.g. a toggle was turned on). Updates the prefs
+   * state and syncs it with the `settings-prefs` singleton.
+   */
+  private syncPrefChange_(event: UserActionSettingPrefChangeEvent): void {
+    const {prefKey, value} = event.detail;
+    this.set(`prefs.${prefKey}.value`, value);
+  }
+
+  /**
    * Called when a menu item is selected.
    */
   private onMenuItemSelected_(e: CustomEvent<{selected: string}>): void {
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/common/testing/e2e_test_base.js
index a78db52..61e867f 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/testing/e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/testing/e2e_test_base.js
@@ -38,7 +38,7 @@
   #include "chrome/common/extensions/extension_constants.h"
   #include "content/public/test/browser_test.h"
   #include "content/public/test/browser_test_utils.h"
-  #include "extensions/browser/extension_host.h"
+  #include "extensions/browser/test_extension_console_observer.h"
   #include "extensions/browser/process_manager.h"
       `);
   }
@@ -62,7 +62,7 @@
   testGenPostamble() {
     GEN(`
     if (fail_on_console_error) {
-      EXPECT_EQ(0u, console_observer.messages().size())
+      EXPECT_EQ(0u, console_observer.GetErrorCount())
           << "Found console.warn or console.error with message: "
           << console_observer.GetMessageAt(0);
     }
@@ -76,31 +76,18 @@
     GEN(`
     WaitForExtension(extension_misc::${extensionIdName}, std::move(load_cb));
 
-    extensions::ExtensionHost* host =
-        extensions::ProcessManager::Get(GetProfile())
-            ->GetBackgroundHostForExtension(
-                extension_misc::${extensionIdName});
-
     bool fail_on_console_error = ${failOnConsoleError};
     // Convert |allowedMessages| into a C++ set.
     base::flat_set<std::u16string> allowed_messages({${messages}});
-    content::WebContentsConsoleObserver console_observer(host->host_contents());
+    extensions::TestExtensionConsoleObserver
+        console_observer(GetProfile(), extension_misc::${extensionIdName},
+        fail_on_console_error);
     // In most cases, A11y extensions should not log warnings or errors.
     // However, informational messages may be logged in some cases and should
     // be specified in |allowed_messages|. All other messages should cause test
     // failures.
-    auto filter =
-        [](const base::flat_set<std::u16string>& allowed,
-           const content::WebContentsConsoleObserver::Message& message) {
-          if (allowed.contains(message.message))
-            return false;
-
-          return message.log_level ==
-              blink::mojom::ConsoleMessageLevel::kWarning ||
-              message.log_level == blink::mojom::ConsoleMessageLevel::kError;
-        };
     if (fail_on_console_error) {
-      console_observer.SetFilter(base::BindRepeating(filter, allowed_messages));
+      console_observer.SetAllowedErrorMessages(allowed_messages);
     }
     `);
   }
diff --git a/chrome/browser/resources/commerce/product_specifications/BUILD.gn b/chrome/browser/resources/commerce/product_specifications/BUILD.gn
index 40ea30c2..663574e 100644
--- a/chrome/browser/resources/commerce/product_specifications/BUILD.gn
+++ b/chrome/browser/resources/commerce/product_specifications/BUILD.gn
@@ -12,6 +12,7 @@
   # Files holding a Polymer element definition and have an equivalent .html file.
   web_component_files = [
     "app.ts",
+    "product_selector.ts",
     "table.ts",
   ]
 
@@ -22,6 +23,7 @@
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources/cr_components/color_change_listener:build_ts",
     "//ui/webui/resources/cr_components/commerce:build_ts",
+    "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
diff --git a/chrome/browser/resources/commerce/product_specifications/app.ts b/chrome/browser/resources/commerce/product_specifications/app.ts
index 3c69745..9a97842 100644
--- a/chrome/browser/resources/commerce/product_specifications/app.ts
+++ b/chrome/browser/resources/commerce/product_specifications/app.ts
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import '../strings.m.js';
+import './product_selector.js';
 import './table.js';
 
 import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
@@ -74,7 +75,7 @@
     this.specsTable_ = {
       columns: productSpecs.products.map(p => {
         return {
-          title: p.title,
+          selectedItem: {title: p.title, url: '', imageUrl: p.imageUrl.url},
         };
       }),
       rows,
diff --git a/chrome/browser/resources/commerce/product_specifications/product_selector.html b/chrome/browser/resources/commerce/product_specifications/product_selector.html
new file mode 100644
index 0000000..43e95cf
--- /dev/null
+++ b/chrome/browser/resources/commerce/product_specifications/product_selector.html
@@ -0,0 +1,61 @@
+<style include="cr-shared-style">
+  :host {
+    --product-selector-width: 250px;
+  }
+
+  #currentProductContainer,
+  #selectionContainer {
+    box-sizing: border-box;
+    display: block;
+    overflow: clip;
+    width: var(--product-selector-width);
+  }
+
+  #currentProductContainer {
+    border-radius: 10px;
+    height: 50px;
+  }
+
+  #selectionContainer {
+    padding: 0 8px;
+  }
+
+  cr-action-menu {
+    --cr-menu-border-radius: 10px;
+  }
+
+  #currentItemButton {
+    --cr-section-vertical-padding: 0;
+  }
+</style>
+
+<div id="currentProductContainer">
+  <cr-expand-button id="currentItemButton" on-click="onShowMenu" no-hover>
+    <cr-url-list-item id="currentProduct" size="medium"
+        url="[[selectedItem.url]]" title="[[selectedItem.title]]"
+        description="[[selectedItem.url]]" no-hover>
+    </cr-url-list-item>
+  </cr-expand-button>
+</div>
+
+<cr-lazy-render id="productSelectionMenu">
+  <template>
+    <cr-action-menu>
+      <div id="selectionContainer">
+        <template is="dom-if" if="[[openTabs.length]]" restamp>
+          <cr-expand-button expanded="{{openTabsExpanded}}" no-hover>
+            $i18n{openTabsSectionTitle}
+          </cr-expand-button>
+          <iron-collapse opened="[[openTabsExpanded]]">
+            <template is="dom-repeat" items="[[openTabs]]">
+              <cr-url-list-item class="dropdown-item" size="medium"
+                  url="[[item.url]]" title="[[item.title]]"
+                  description="[[item.url]]" no-hover>
+              </cr-url-list-item>
+            </template>
+          </iron-collapse>
+        </template>
+      </div>
+    </cr-action-menu>
+  </template>
+</cr-lazy-render>
diff --git a/chrome/browser/resources/commerce/product_specifications/product_selector.ts b/chrome/browser/resources/commerce/product_specifications/product_selector.ts
new file mode 100644
index 0000000..33ef74c9
--- /dev/null
+++ b/chrome/browser/resources/commerce/product_specifications/product_selector.ts
@@ -0,0 +1,92 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
+import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
+import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+
+import type {BrowserProxy} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
+import {BrowserProxyImpl} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
+import {AnchorAlignment} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrExpandButtonElement} from 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
+import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import type {CrUrlListItemElement} from 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
+import {mojoString16ToString} from 'chrome://resources/js/mojo_type_util.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './product_selector.html.js';
+
+export interface UrlListEntry {
+  title: string;
+  url: string;
+  imageUrl: string;
+}
+
+export interface ProductSelectorElement {
+  $: {
+    currentItemButton: CrExpandButtonElement,
+    currentProduct: CrUrlListItemElement,
+    currentProductContainer: HTMLElement,
+    productSelectionMenu: CrLazyRenderElement<CrActionMenuElement>,
+  };
+}
+
+export class ProductSelectorElement extends PolymerElement {
+  static get is() {
+    return 'product-selector';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {
+      selectedItem: Object,
+      openTabs: {
+        type: Array,
+        value: () => [],
+      },
+      openTabsExpanded: {
+        type: Boolean,
+        value: true,
+      },
+    };
+  }
+
+  private shoppingApi_: BrowserProxy = BrowserProxyImpl.getInstance();
+
+  selectedItem: UrlListEntry;
+  openTabs: UrlListEntry[];
+
+  private openTabsExpanded: boolean;
+
+  private async onShowMenu() {
+    const {urlInfos} = await this.shoppingApi_.getUrlInfosForOpenTabs();
+    this.openTabs = urlInfos.map(({title, url}) => ({
+                                   title: mojoString16ToString(title),
+                                   url: url.url,
+                                   imageUrl: url.url,
+                                 }));
+
+    const rect = this.$.currentProductContainer.getBoundingClientRect();
+    this.$.productSelectionMenu.get().showAt(this.$.currentProductContainer, {
+      anchorAlignmentX: AnchorAlignment.CENTER,
+      top: rect.bottom,
+      left: rect.left,
+    });
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'product-selector': ProductSelectorElement;
+  }
+}
+
+customElements.define(ProductSelectorElement.is, ProductSelectorElement);
diff --git a/chrome/browser/resources/commerce/product_specifications/table.html b/chrome/browser/resources/commerce/product_specifications/table.html
index 7ff3002..7534fb0 100644
--- a/chrome/browser/resources/commerce/product_specifications/table.html
+++ b/chrome/browser/resources/commerce/product_specifications/table.html
@@ -4,6 +4,7 @@
   }
   .col {
     width: 220px;
+    text-align: start;
   }
   .col-card {
     display: flex;
@@ -16,11 +17,13 @@
     padding-bottom: 24px;
   }
 </style>
+
 <table>
   <thead>
     <template is="dom-repeat" items="[[columns]]" as="column">
       <th class="col">
-        <div class="col-card">[[column.title]]</div>
+        <product-selector selected-item="[[column.selectedItem]]">
+        </product-selector>
       </th>
     </template>
   </thead>
diff --git a/chrome/browser/resources/commerce/product_specifications/table.ts b/chrome/browser/resources/commerce/product_specifications/table.ts
index c2ed1832..86c4acf6 100644
--- a/chrome/browser/resources/commerce/product_specifications/table.ts
+++ b/chrome/browser/resources/commerce/product_specifications/table.ts
@@ -4,6 +4,7 @@
 
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import type {UrlListEntry} from './product_selector.js';
 import {getTemplate} from './table.html.js';
 
 /** Describes a row in a ProductSpecs table. */
@@ -13,7 +14,7 @@
 }
 /** Describes a column in a ProductSpecs table. */
 export interface TableColumn {
-  title: string;
+  selectedItem: UrlListEntry;
 }
 
 /** Element for rendering a ProductSpecs table. */
diff --git a/chrome/browser/resources/compose/app.html b/chrome/browser/resources/compose/app.html
index 3733c95..5457fc1 100644
--- a/chrome/browser/resources/compose/app.html
+++ b/chrome/browser/resources/compose/app.html
@@ -199,7 +199,7 @@
     border-bottom: solid 1px var(--color-compose-dialog-divider);
   }
 
-  :host(:not([enable-ui-refinements])) :host([loading-indicator-shown_]) #body {
+  :host(:not([enable-ui-refinements])[loading-indicator-shown_]) #body {
     /* When loading, the heights of contents within #body are in flux due to
     * animations. Force the entire #body to be visible to prevent scrollbars
     * from flickering, and remove scrollbar width from padding. */
diff --git a/chrome/browser/resources/downloads/icons.html b/chrome/browser/resources/downloads/icons.html
index 1232f1aa..ad3f0bb6 100644
--- a/chrome/browser/resources/downloads/icons.html
+++ b/chrome/browser/resources/downloads/icons.html
@@ -10,6 +10,10 @@
           d="M 8.25 21 L 3 15.75 L 3 8.25 L 8.25 3 L 15.75 3 L 21 8.25 L 21 15.75 L 15.75 21 Z M 9.148438 16.25 L 12 13.398438 L 14.851562 16.25 L 16.25 14.851562 L 13.398438 12 L 16.25 9.148438 L 14.851562 7.75 L 12 10.601562 L 9.148438 7.75 L 7.75 9.148438 L 10.601562 12 L 7.75 14.851562 Z M 9.148438 16.25">
         </path>
       </g>
+      <g id="shield">
+        <path fill="#0b57d0" fill-opacity="1" fill-rule="nonzero" d="M12 22.210938c-2.375-.597657-4.335938-1.949219-5.882812-4.066407-1.546876-2.113281-2.320313-4.460937-2.320313-7.042969V4.859375L12 1.789062l8.203125 3.070313v6.242187c0 2.582032-.773437 4.929688-2.320313 7.042969C16.335938 20.261719 14.375 21.613281 12 22.210938Zm0-2.375c1.589844-.527344 2.917969-1.511719 3.992188-2.964844C17.066406 15.421875 17.695312 13.796875 17.878906 12H12V4.207031L6.070312 6.433594v5.117187c0 .125.019532.273438.050782.449219H12Zm0 0">
+        </path>
+      </g>
     </defs>
   </svg>
 </iron-iconset-svg>
diff --git a/chrome/browser/resources/downloads/item.html b/chrome/browser/resources/downloads/item.html
index a11b9cef..a5491fb 100644
--- a/chrome/browser/resources/downloads/item.html
+++ b/chrome/browser/resources/downloads/item.html
@@ -604,7 +604,7 @@
     <!-- TODO(crbug.com/333734490): Replace icon with branded gshield icon. -->
     <cr-link-row
         id="esb-download-row-promo"
-        start-icon="downloads:dangerous"
+        start-icon="downloads:shield"
         external
         on-click="onEsbPromotionClick_"
         button-aria-description="$i18n{esbDownloadRowPromoA11y}"
diff --git a/chrome/browser/resources/history/BUILD.gn b/chrome/browser/resources/history/BUILD.gn
index 82a5cc89..be60413 100644
--- a/chrome/browser/resources/history/BUILD.gn
+++ b/chrome/browser/resources/history/BUILD.gn
@@ -55,6 +55,7 @@
 
   ts_deps = [
     "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources/cr_components/history:build_ts",
     "//ui/webui/resources/cr_components/history_clusters:build_ts",
     "//ui/webui/resources/cr_components/history_embeddings:build_ts",
     "//ui/webui/resources/cr_components/managed_footnote:build_ts",
diff --git a/chrome/browser/resources/history/app.ts b/chrome/browser/resources/history/app.ts
index 14de854..c260f67 100644
--- a/chrome/browser/resources/history/app.ts
+++ b/chrome/browser/resources/history/app.ts
@@ -19,8 +19,9 @@
 import './side_bar.js';
 import './strings.m.js';
 
-import type {HistoryEmbeddingsMoreActionsClickEvent} from 'chrome://resources/cr_components/history_embeddings/history_embeddings.js';
+import {HistoryResultType} from 'chrome://resources/cr_components/history/constants.js';
 import type {Suggestion} from 'chrome://resources/cr_components/history_embeddings/filter_chips.js';
+import type {HistoryEmbeddingsMoreActionsClickEvent} from 'chrome://resources/cr_components/history_embeddings/history_embeddings.js';
 import type {CrDrawerElement} from 'chrome://resources/cr_elements/cr_drawer/cr_drawer.js';
 import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import type {FindShortcutMixinInterface} from 'chrome://resources/cr_elements/find_shortcut_mixin.js';
@@ -290,6 +291,9 @@
         document, 'keydown', (e: Event) => this.onKeyDown_(e as KeyboardEvent));
     this.eventTracker_.add(
         document, 'visibilitychange', this.onVisibilityChange_.bind(this));
+    this.eventTracker_.add(
+        document, 'record-history-link-click',
+        this.onRecordHistoryLinkClick_.bind(this));
     this.addWebUiListener(
         'sign-in-state-changed',
         (signedIn: boolean) => this.onSignInStateChanged_(signedIn));
@@ -462,6 +466,48 @@
     }
   }
 
+  private onRecordHistoryLinkClick_(
+      e: CustomEvent<{resultType: HistoryResultType, index: number}>) {
+    // All of the above code only applies to History search results, not the
+    // zero-query state. Check queryResult_ instead of queryState_ to key on
+    // actually displayed results rather than the latest user input, which may
+    // not have finished loading yet.
+    if (!this.queryResult_.info || !this.queryResult_.info.term) {
+      return;
+    }
+
+    this.browserService_!.recordHistogram(
+        'History.SearchResultClicked.Type', e.detail.resultType,
+        HistoryResultType.END);
+
+    // MetricsHandler uses a 100 bucket limit, so the max index is 99.
+    const maxIndex = 99;
+    const clampedIndex = Math.min(e.detail.index, 99);
+    this.browserService_!.recordHistogram(
+        'History.SearchResultClicked.Index', clampedIndex, maxIndex);
+
+    switch (e.detail.resultType) {
+      case HistoryResultType.TRADITIONAL: {
+        this.browserService_!.recordHistogram(
+            'History.SearchResultClicked.Index.Traditional', clampedIndex,
+            maxIndex);
+        break;
+      }
+      case HistoryResultType.GROUPED: {
+        this.browserService_!.recordHistogram(
+            'History.SearchResultClicked.Index.Grouped', clampedIndex,
+            maxIndex);
+        break;
+      }
+      case HistoryResultType.EMBEDDINGS: {
+        this.browserService_!.recordHistogram(
+            'History.SearchResultClicked.Index.Embeddings', clampedIndex,
+            maxIndex);
+        break;
+      }
+    }
+  }
+
   private onDeleteCommand_() {
     if (this.$.toolbar.count === 0 || this.pendingDelete_) {
       return;
diff --git a/chrome/browser/resources/history/history_item.html b/chrome/browser/resources/history/history_item.html
index b2aeff133..1fd97b3c 100644
--- a/chrome/browser/resources/history/history_item.html
+++ b/chrome/browser/resources/history/history_item.html
@@ -220,7 +220,7 @@
               <a href="[[item.url]]" id="link" class="website-link"
                   focus-row-control focus-type="link"
                   title="[[item.title]]" on-click="onLinkClick_"
-                  on-contextmenu="onLinkRightClick_"
+                  on-auxclick="onLinkClick_" on-contextmenu="onLinkRightClick_"
                   aria-describedby$="[[ariaDescribedByForHeading_]]">
                 <div class="website-icon" id="icon"></div>
                 <history-searched-label class="website-title"
diff --git a/chrome/browser/resources/history/history_item.ts b/chrome/browser/resources/history/history_item.ts
index f618a26..c1c2c47 100644
--- a/chrome/browser/resources/history/history_item.ts
+++ b/chrome/browser/resources/history/history_item.ts
@@ -10,6 +10,7 @@
 import 'chrome://resources/js/icon.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 
+import {HistoryResultType} from 'chrome://resources/cr_components/history/constants.js';
 import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import type {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import {FocusRowMixin} from 'chrome://resources/cr_elements/focus_row_mixin.js';
@@ -308,6 +309,11 @@
     if (this.searchTerm) {
       browserService.recordAction('SearchResultClick');
     }
+
+    this.fire_('record-history-link-click', {
+      resultType: HistoryResultType.TRADITIONAL,
+      index: this.index,
+    });
   }
 
   private onLinkRightClick_() {
diff --git a/chrome/browser/resources/side_panel/customize_chrome/categories.html b/chrome/browser/resources/side_panel/customize_chrome/categories.html
index bcfa79f3..9a420c5 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/categories.html
+++ b/chrome/browser/resources/side_panel/customize_chrome/categories.html
@@ -232,7 +232,7 @@
       <customize-chrome-check-mark-wrapper
           checked="[[isClassicChromeSelected_]]">
         <div id="cornerNewTabPageTile" class="image-container">
-          <img id="cornerNewTabPage" src="icons/gm3_corner_new_tab_page.svg">
+          <img id="cornerNewTabPage" src="icons/corner_new_tab_page.svg">
         </div>
       </customize-chrome-check-mark-wrapper>
       <div class="label">$i18n{classicChrome}</div>
diff --git a/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn b/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn
index 260c2c6..5d22040 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn
+++ b/chrome/browser/resources/side_panel/customize_chrome/icons/BUILD.gn
@@ -10,20 +10,20 @@
   grd_prefix = "side_panel_customize_chrome"
   out_grd = "$target_gen_dir/resources.grdp"
   input_files = [
-    "collapse_carets.svg",
     "chrome_web_store.svg",
+    "collapse_carets.svg",
+    "corner_new_tab_page.svg",
     "coupons.svg",
     "delete.svg",
     "expand_carets.svg",
     "generated_image.svg",
-    "gm3_corner_new_tab_page.svg",
-    "gm3_mini_new_tab_page.svg",
     "image.svg",
+    "mini_new_tab_page.svg",
     "productivity.svg",
     "reset.svg",
     "sparkle.svg",
-    "upload.svg",
     "uploaded_image.svg",
+    "upload.svg",
     "writing.svg",
   ]
   input_files_base_dir = rebase_path(".", "//")
diff --git a/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_corner_new_tab_page.svg b/chrome/browser/resources/side_panel/customize_chrome/icons/corner_new_tab_page.svg
similarity index 100%
rename from chrome/browser/resources/side_panel/customize_chrome/icons/gm3_corner_new_tab_page.svg
rename to chrome/browser/resources/side_panel/customize_chrome/icons/corner_new_tab_page.svg
diff --git a/chrome/browser/resources/side_panel/customize_chrome/icons/gm3_mini_new_tab_page.svg b/chrome/browser/resources/side_panel/customize_chrome/icons/mini_new_tab_page.svg
similarity index 100%
rename from chrome/browser/resources/side_panel/customize_chrome/icons/gm3_mini_new_tab_page.svg
rename to chrome/browser/resources/side_panel/customize_chrome/icons/mini_new_tab_page.svg
diff --git a/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html b/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html
index c767e6b2..7a415e04 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html
+++ b/chrome/browser/resources/side_panel/customize_chrome/theme_snapshot.html
@@ -30,7 +30,7 @@
     justify-content: center;
   }
 
-  #gm3MiniNewTabPage {
+  #miniNewTabPage {
     height: auto;
     width: 100%;
   }
@@ -107,9 +107,9 @@
     <div class="image-background image"
         id="classicChromeBackground"
         on-click="onThemeSnapshotClick_">
-      <svg id="gm3MiniNewTabPage" aria-labelledby="classicChromeThemeTitle"
+      <svg id="miniNewTabPage" aria-labelledby="classicChromeThemeTitle"
           viewBox="0 0 240 126" preserveAspectRatio="xMidYMid meet">
-        <use href="icons/gm3_mini_new_tab_page.svg#miniNewTabPage"></use>
+        <use href="icons/mini_new_tab_page.svg#miniNewTabPage"></use>
       </svg>
       <div class="overlay"></div>
       <cr-ripple></cr-ripple>
diff --git a/chrome/browser/resources/side_panel/read_anything/voice_selection_menu.html b/chrome/browser/resources/side_panel/read_anything/voice_selection_menu.html
index 0d4299b..d727e06 100644
--- a/chrome/browser/resources/side_panel/read_anything/voice_selection_menu.html
+++ b/chrome/browser/resources/side_panel/read_anything/voice_selection_menu.html
@@ -35,7 +35,7 @@
   }
   .dropdown-voice-selection-button:hover {
     background-color: var(--cr-hover-background-color);
-    cursor:pointer;
+    cursor: pointer;
   }
   .dropdown-voice-selection-button:focus-visible {
     background-color: var(--cr-hover-background-color);
@@ -92,14 +92,14 @@
                   icon="read-anything-20:check-mark"></cr-icon>
                 [[item.title]]
               </span>
-              <!-- TODO(b/333958820): Ensure keyboard focus is handled correctly
-                   when the play button is pressed. -->
-              <cr-icon-button id="play-icon" on-click="onVoicePreviewClick_"
-                title="$i18n{playLabel}"
-                disabled$="[[item.previewPlaying]]"
-                aria-label="$i18n{playLabel} [[item.title]]"
-                iron-icon="read-anything-20:play-circle"></cr-icon-button>
+              <cr-icon-button id="preview-icon" on-click="onVoicePreviewClick_"
+                title$="[[previewLabel_(item.previewPlaying,'')]]"
+                aria-label$="[[previewLabel_(item.previewPlaying,item.title)]]"
+                iron-icon="[[previewIcon_(item.previewPlaying)]]">
+              </cr-icon-button>
             </button>
+          <!-- TODO(crbug.com/1474951) Pause the preview when pause button is
+                    pressed -->
           </template>
         </div>
       </template>
diff --git a/chrome/browser/resources/side_panel/read_anything/voice_selection_menu.ts b/chrome/browser/resources/side_panel/read_anything/voice_selection_menu.ts
index af5b13e..442066e6 100644
--- a/chrome/browser/resources/side_panel/read_anything/voice_selection_menu.ts
+++ b/chrome/browser/resources/side_panel/read_anything/voice_selection_menu.ts
@@ -14,6 +14,7 @@
 import type {CrLazyRenderElement} from '//resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {WebUiListenerMixin} from '//resources/cr_elements/web_ui_listener_mixin.js';
 import {assert} from '//resources/js/assert.js';
+import {loadTimeData} from '//resources/js/load_time_data.js';
 import type {DomRepeatEvent} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -152,14 +153,19 @@
     // line is to make sure that the voice-selection callback is not triggered.
     event.stopImmediatePropagation();
 
-    const previewVoice = event.model.item.voice;
-    this.dispatchEvent(new CustomEvent('preview-voice', {
-      bubbles: true,
-      composed: true,
-      detail: {
-        previewVoice,
-      },
-    }));
+    const button = event.target as HTMLElement;
+    assert(button, 'no target for preview');
+
+    if (!event.model.item.previewPlaying) {
+      const previewVoice = event.model.item.voice;
+      this.dispatchEvent(new CustomEvent('preview-voice', {
+        bubbles: true,
+        composed: true,
+        detail: {
+          previewVoice,
+        },
+      }));
+    }
   }
 
   private onClose_() {
@@ -179,10 +185,9 @@
         (currentElement.classList.contains('dropdown-voice-selection-button')) ?
         true :
         false;
-    const targetIsPreviewButton = (currentElement.id === 'play-icon' ||
-                                   currentElement.id === 'pause-icon') ?
-        true :
-        false;
+    const targetIsPreviewButton =
+        (currentElement.id === 'preview-icon') ? true : false;
+
     // For voice options, only handle the right arrow - everything else is
     // default
     if (targetIsVoiceOption && !['ArrowRight'].includes(e.key)) {
@@ -193,6 +198,7 @@
         !['ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
       return;
     }
+
     // When the menu first opens, the target is the whole menu.
     // In that case, use default behavior.
     if (!targetIsVoiceOption && !targetIsPreviewButton) return;
@@ -200,16 +206,36 @@
     e.preventDefault();
 
     if (targetIsVoiceOption) {
-      // From a voice option, go to whichever preview button is visible
-      // There are 'play-icon' and 'pause-icon' preview buttons, and
-      // only one is visible at a time. The one that's visible has style
-      // display-true, so use that to directly select the right button
+      // From a voice option, go to its preview button
       const visiblePreviewButton =
-          currentElement.querySelector<HTMLElement>('#play-icon');
+          currentElement.querySelector<HTMLElement>('#preview-icon');
       assert(visiblePreviewButton, 'can\'t find preview button');
       visiblePreviewButton!.focus();
-    } else {  // Voice preview button - go to voice entry
-      currentElement.parentElement!.focus();
+    }
+    // This action is also handled by the menu itself
+    // For left arrow, this takes us to the voice being previewed,
+    // For up and down arrows this is combined with the default up/down
+    // action, taking us to the next or previous voice.
+    currentElement.parentElement!.focus();
+  }
+
+  private previewLabel_(previewPlaying: boolean, voiceName: string): string {
+    let nameSuffix = '';
+    if (voiceName.length > 0) {
+      nameSuffix = ' ' + voiceName;
+    }
+    if (previewPlaying) {
+      return loadTimeData.getString('pauseLabel') + nameSuffix;
+    } else {
+      return loadTimeData.getString('playLabel') + nameSuffix;
+    }
+  }
+
+  private previewIcon_(previewPlaying: boolean): string {
+    if (previewPlaying) {
+      return 'read-anything-20:pause-circle';
+    } else {
+      return 'read-anything-20:play-circle';
     }
   }
 }
diff --git a/chrome/browser/resources/welcome/BUILD.gn b/chrome/browser/resources/welcome/BUILD.gn
index 350a7df..d26bff9 100644
--- a/chrome/browser/resources/welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/BUILD.gn
@@ -58,9 +58,6 @@
     extra_grdp_files = [ "$target_gen_dir/icon_resources.grdp" ]
   }
 
-  # Files holding a Polymer element definition AND have an equivalent .html file.
-  web_component_files = [ "set_as_default/nux_set_as_default.ts" ]
-
   non_web_component_files = [
     "landing_view.html.ts",
     "landing_view_proxy.ts",
@@ -82,7 +79,9 @@
     "ntp_background/ntp_background_proxy.ts",
     "ntp_background/nux_ntp_background.html.ts",
     "ntp_background/nux_ntp_background.ts",
+    "set_as_default/nux_set_as_default.html.ts",
     "set_as_default/nux_set_as_default_proxy.ts",
+    "set_as_default/nux_set_as_default.ts",
     "shared/bookmark_proxy.ts",
     "shared/module_metrics_proxy.ts",
     "shared/nux_types.ts",
diff --git a/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.html b/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.html
deleted file mode 100644
index 407c48c..0000000
--- a/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<div class="container">
-  <h1 tabindex="-1">${this.subtitle}</h1>
-  <h2>$i18n{setDefaultSubHeader}</h2>
-  <div class="illustration slide-in" aria-hidden="true"></div>
-  <div class="button-bar">
-    <cr-button id="declineButton" @click="${this.onDeclineClick_}">
-      $i18n{skip}
-    </cr-button>
-    <step-indicator .model="${this.indicatorModel}"></step-indicator>
-    <cr-button class="action-button" @click="${this.onSetDefaultClick_}">
-      $i18n{setDefaultConfirm}
-<if expr="is_win">
-  <iron-icon icon="cr:open-in-new" slot="suffix-icon"
-      ?hidden="${!this.isWin10_}">
-  </iron-icon>
-</if>
-    </cr-button>
-  </div>
-</div>
diff --git a/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.html.ts b/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.html.ts
new file mode 100644
index 0000000..487aa83
--- /dev/null
+++ b/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.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 {NuxSetAsDefaultElement} from './nux_set_as_default.js';
+
+export function getHtml(this: NuxSetAsDefaultElement) {
+  return html`<!--_html_template_start_-->
+<div class="container">
+  <h1 tabindex="-1">${this.subtitle}</h1>
+  <h2>$i18n{setDefaultSubHeader}</h2>
+  <div class="illustration slide-in" aria-hidden="true"></div>
+  <div class="button-bar">
+    <cr-button id="declineButton" @click="${this.onDeclineClick_}">
+      $i18n{skip}
+    </cr-button>
+    <step-indicator .model="${this.indicatorModel}"></step-indicator>
+    <cr-button class="action-button" @click="${this.onSetDefaultClick_}">
+      $i18n{setDefaultConfirm}
+<if expr="is_win">
+      <iron-icon icon="cr:open-in-new" slot="suffix-icon"
+          ?hidden="${!this.isWin10_}">
+      </iron-icon>
+</if>
+    </cr-button>
+  </div>
+</div>
+<!--_html_template_end_-->`;
+}
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_dialog_browsertest.cc b/chrome/browser/search_engine_choice/search_engine_choice_dialog_browsertest.cc
index 000189a..8e2f88a 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_dialog_browsertest.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_dialog_browsertest.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/common/chrome_constants.h"
@@ -270,6 +271,7 @@
   }
 
  private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::AutoReset<bool> scoped_chrome_build_override_ =
       SearchEngineChoiceDialogServiceFactory::
           ScopedChromeBuildOverrideForTesting(
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_config.cc b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
index fd89c4e..c8529e6 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_config.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
@@ -165,6 +165,7 @@
   configs.emplace_back(TabletProductivityUserModel::GetConfig());
   configs.emplace_back(MostVisitedTilesUser::GetConfig());
   configs.emplace_back(AndroidHomeModuleRanker::GetConfig());
+  configs.emplace_back(GetConfigForWebAppInstallationPromo());
 #endif
   configs.emplace_back(LowUserEngagementModel::GetConfig());
   configs.emplace_back(SearchUserModel::GetConfig());
@@ -181,9 +182,7 @@
   configs.emplace_back(OptimizationTargetSegmentationDummy::GetConfig());
 
   if (base::FeatureList::IsEnabled(
-          webapps::features::kWebAppsEnableMLModelForPromotion) ||
-      base::FeatureList::IsEnabled(
-          webapps::features::kInstallPromptSegmentation)) {
+          webapps::features::kWebAppsEnableMLModelForPromotion)) {
     configs.emplace_back(GetConfigForWebAppInstallationPromo());
   }
   if (base::FeatureList::IsEnabled(ntp_features::kNtpDriveModuleSegmentation)) {
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index bf8b725..5b335c9 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -69,6 +69,7 @@
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
@@ -3424,6 +3425,9 @@
     web_app_info->tab_strip = std::move(tab_strip);
     return web_app::test::InstallWebApp(profile, std::move(web_app_info));
   }
+
+ private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 // This is disabled on mac pending http://crbug.com/1194201
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java
index 4fff254..e50021cd 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackRenderTest.java
@@ -39,8 +39,6 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -116,8 +114,6 @@
     @Test
     @LargeTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testCaptureTop() throws Exception {
         View view = mTab.getView();
         Size size = new Size(view.getWidth(), view.getHeight());
@@ -131,8 +127,6 @@
     @Test
     @LargeTest
     @Feature({"RenderTest"})
-    // TODO(crbug.com/1453741): Fix test with CREATE_NEW_TAB_INITIALIZE_RENDERER.
-    @DisableFeatures(ChromeFeatureList.CREATE_NEW_TAB_INITIALIZE_RENDERER)
     public void testCaptureBottom() throws Exception {
         RenderCoordinates renderCoordinates =
                 RenderCoordinates.fromWebContents(mTab.getWebContents());
diff --git a/chrome/browser/shortcuts/BUILD.gn b/chrome/browser/shortcuts/BUILD.gn
index 805fad0..578a615 100644
--- a/chrome/browser/shortcuts/BUILD.gn
+++ b/chrome/browser/shortcuts/BUILD.gn
@@ -9,13 +9,44 @@
 source_set("shortcuts") {
   sources = [
     "create_shortcut_for_current_web_contents_task.h",
+    "document_icon_fetcher.cc",
+    "document_icon_fetcher.h",
+    "fetch_icons_from_document_task.cc",
+    "fetch_icons_from_document_task.h",
     "platform_util_mac.h",
     "platform_util_mac.mm",
   ]
 
   deps = [
     "//base",
+    "//components/webapps/common",
+    "//components/webapps/common:mojo_bindings",
     "//content/public/browser",
+    "//mojo/public/cpp/bindings",
+    "//skia",
+    "//third_party/blink/public/common",
+    "//ui/gfx",
+    "//url",
+  ]
+}
+
+source_set("browser_tests") {
+  testonly = true
+
+  sources = [ "document_icon_fetcher_browsertest.cc" ]
+
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+  deps = [
+    ":shortcuts",
+    "//base",
+    "//chrome/browser/ui:ui",
+    "//chrome/test:test_support_ui",
+    "//content/public/browser",
+    "//skia",
+    "//testing/gtest:gtest",
+    "//ui/gfx:test_support",
+    "//url",
   ]
 }
 
diff --git a/chrome/browser/shortcuts/document_icon_fetcher.cc b/chrome/browser/shortcuts/document_icon_fetcher.cc
new file mode 100644
index 0000000..9ae1be2
--- /dev/null
+++ b/chrome/browser/shortcuts/document_icon_fetcher.cc
@@ -0,0 +1,81 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/shortcuts/document_icon_fetcher.h"
+
+#include <iterator>
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_forward.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/types/expected.h"
+#include "base/types/pass_key.h"
+#include "chrome/browser/shortcuts/fetch_icons_from_document_task.h"
+#include "content/public/browser/document_user_data.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace shortcuts {
+
+// static
+void DocumentIconFetcher::FetchIcons(content::WebContents& web_contents,
+                                     FetchIconsFromDocumentCallback callback) {
+  DocumentIconFetcher* fetch_manager =
+      DocumentIconFetcher::GetOrCreateForCurrentDocument(
+          web_contents.GetPrimaryMainFrame());
+  fetch_manager->RunTask(std::move(callback));
+}
+
+DocumentIconFetcher::~DocumentIconFetcher() {
+  in_destruction_ = true;
+  // All of the tasks callbacks call `DocumentIconFetcher::OnTaskComplete` below
+  // directly. Save them & call them synchronously after the tasks are
+  // destroyed to prevent map modification during iteration.
+  std::vector<FetchIconsFromDocumentCallback> pending_callbacks;
+  for (const auto& [id, task] : fetch_tasks_) {
+    pending_callbacks.push_back(task->TakeCallback());
+  }
+  fetch_tasks_.clear();
+  for (FetchIconsFromDocumentCallback& callback : pending_callbacks) {
+    CHECK(callback);
+    // This calls `DocumentIconFetcher::OnTaskComplete` below.
+    std::move(callback).Run(
+        base::unexpected(FetchIconsForDocumentError::kDocumentDestroyed));
+  }
+}
+
+DOCUMENT_USER_DATA_KEY_IMPL(DocumentIconFetcher);
+
+DocumentIconFetcher::DocumentIconFetcher(content::RenderFrameHost* rfh)
+    : content::DocumentUserData<DocumentIconFetcher>(rfh) {}
+
+void DocumentIconFetcher::RunTask(FetchIconsFromDocumentCallback callback) {
+  std::unique_ptr<FetchIconsFromDocumentTask> task =
+      std::make_unique<FetchIconsFromDocumentTask>(
+          base::PassKey<DocumentIconFetcher>(), render_frame_host());
+  int task_id = next_task_id_;
+  next_task_id_++;
+  const auto& [iter, _] = fetch_tasks_.emplace(task_id, std::move(task));
+  // Note: the callback may be called synchronously.
+  iter->second->Start(base::BindOnce(&DocumentIconFetcher::OnTaskComplete,
+                                     weak_factory_.GetWeakPtr(), task_id,
+                                     std::move(callback)));
+}
+
+void DocumentIconFetcher::OnTaskComplete(
+    int id,
+    FetchIconsFromDocumentCallback original_callback,
+    FetchIconsFromDocumentResult result) {
+  int num_erased = fetch_tasks_.erase(id);
+  CHECK(num_erased > 0 || in_destruction_);
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(original_callback), result));
+}
+
+}  // namespace shortcuts
diff --git a/chrome/browser/shortcuts/document_icon_fetcher.h b/chrome/browser/shortcuts/document_icon_fetcher.h
new file mode 100644
index 0000000..f057bbe
--- /dev/null
+++ b/chrome/browser/shortcuts/document_icon_fetcher.h
@@ -0,0 +1,56 @@
+// 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_SHORTCUTS_DOCUMENT_ICON_FETCHER_H_
+#define CHROME_BROWSER_SHORTCUTS_DOCUMENT_ICON_FETCHER_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/functional/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/shortcuts/fetch_icons_from_document_task.h"
+#include "content/public/browser/document_user_data.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace shortcuts {
+
+// This object is responsible for fetching all available icons from a given
+// document.
+class DocumentIconFetcher
+    : public content::DocumentUserData<DocumentIconFetcher> {
+ public:
+  // Fetches all icons for the top level primary frame of the given web
+  // contents. `callback` will always be called (even on document destruction),
+  // and always called asynchronously.
+  static void FetchIcons(content::WebContents& web_contents,
+                         FetchIconsFromDocumentCallback callback);
+
+  ~DocumentIconFetcher() override;
+
+ private:
+  friend DocumentUserData;
+  DOCUMENT_USER_DATA_KEY_DECL();
+
+  explicit DocumentIconFetcher(content::RenderFrameHost* rfh);
+
+  void RunTask(FetchIconsFromDocumentCallback callback);
+
+  void OnTaskComplete(int id,
+                      FetchIconsFromDocumentCallback original_callback,
+                      FetchIconsFromDocumentResult result);
+
+  bool in_destruction_ = false;
+  int next_task_id_ = 0;
+  base::flat_map<int, std::unique_ptr<FetchIconsFromDocumentTask>> fetch_tasks_;
+
+  base::WeakPtrFactory<DocumentIconFetcher> weak_factory_{this};
+};
+
+}  // namespace shortcuts
+
+#endif  // CHROME_BROWSER_SHORTCUTS_DOCUMENT_ICON_FETCHER_H_
diff --git a/chrome/browser/shortcuts/document_icon_fetcher_browsertest.cc b/chrome/browser/shortcuts/document_icon_fetcher_browsertest.cc
new file mode 100644
index 0000000..b93d0f8
--- /dev/null
+++ b/chrome/browser/shortcuts/document_icon_fetcher_browsertest.cc
@@ -0,0 +1,162 @@
+// 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/shortcuts/document_icon_fetcher.h"
+
+#include <memory>
+
+#include "base/base_paths.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/test/gmock_expected_support.h"
+#include "base/test/test_future.h"
+#include "base/types/expected.h"
+#include "chrome/browser/shortcuts/fetch_icons_from_document_task.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/test/sk_gmock_support.h"
+
+namespace shortcuts {
+namespace {
+constexpr char kPageNoIcons[] = "/shortcuts/no_icons_page.html";
+constexpr char kPageWithIcons[] = "/shortcuts/page_icons.html";
+
+class DocumentIconFetcherTest : public InProcessBrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    default_favicon_server_.AddDefaultHandlers(base::FilePath(
+        FILE_PATH_LITERAL("chrome/test/data/shortcuts/default_icon_has_two")));
+    ASSERT_TRUE(embedded_https_test_server().Start());
+    ASSERT_TRUE(default_favicon_server_.Start());
+  }
+
+  GURL GetPageWithDefaultFavicon() {
+    return default_favicon_server_.GetURL("/index.html");
+  }
+
+  base::expected<SkBitmap, std::string> LoadImageFromTestFile(
+      const base::FilePath& relative_path_from_chrome_data) {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    // Load image data from test directory.
+    base::FilePath chrome_src_dir;
+    if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT,
+                                &chrome_src_dir)) {
+      return base::unexpected("Could not find src directory.");
+    }
+
+    base::FilePath image_path =
+        chrome_src_dir.Append(FILE_PATH_LITERAL("chrome/test/data"))
+            .Append(relative_path_from_chrome_data);
+    if (!base::PathExists(image_path)) {
+      return base::unexpected(
+          base::StrCat({"Path does not exist: ", image_path.AsUTF8Unsafe()}));
+    }
+    std::string image_data;
+    if (!base::ReadFileToString(image_path, &image_data)) {
+      return base::unexpected("Could not read file.");
+    }
+
+    SkBitmap image;
+    if (!gfx::PNGCodec::Decode(
+            reinterpret_cast<const uint8_t*>(image_data.data()),
+            image_data.size(), &image)) {
+      return base::unexpected("Could not decode file.");
+    }
+    return image;
+  }
+
+ private:
+  net::EmbeddedTestServer default_favicon_server_{
+      net::EmbeddedTestServer::TYPE_HTTPS};
+};
+
+IN_PROC_BROWSER_TEST_F(DocumentIconFetcherTest, PageNoIcons) {
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_https_test_server().GetURL(kPageNoIcons)));
+
+  base::test::TestFuture<FetchIconsFromDocumentResult> future;
+  DocumentIconFetcher::FetchIcons(
+      *browser()->tab_strip_model()->GetActiveWebContents(),
+      future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+  EXPECT_TRUE(future.Get().has_value());
+  EXPECT_THAT(future.Get().value(), testing::IsEmpty());
+}
+
+IN_PROC_BROWSER_TEST_F(DocumentIconFetcherTest, IconMetadata) {
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_https_test_server().GetURL(kPageWithIcons)));
+
+  base::test::TestFuture<FetchIconsFromDocumentResult> future;
+
+  DocumentIconFetcher::FetchIcons(
+      *browser()->tab_strip_model()->GetActiveWebContents(),
+      future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+  ASSERT_TRUE(future.Get().has_value());
+  std::vector<SkBitmap> images = future.Get().value();
+
+  SkBitmap expected_green;
+  ASSERT_OK_AND_ASSIGN(expected_green,
+                       LoadImageFromTestFile(base::FilePath(
+                           FILE_PATH_LITERAL("shortcuts/green.png"))));
+  SkBitmap expected_noise;
+  ASSERT_OK_AND_ASSIGN(expected_noise,
+                       LoadImageFromTestFile(base::FilePath(
+                           FILE_PATH_LITERAL("shortcuts/noise.png"))));
+  EXPECT_THAT(images, testing::UnorderedElementsAre(
+                          gfx::test::EqualsBitmap(expected_green),
+                          gfx::test::EqualsBitmap(expected_noise)));
+}
+
+IN_PROC_BROWSER_TEST_F(DocumentIconFetcherTest, DefaultFavicon) {
+  ASSERT_TRUE(
+      ui_test_utils::NavigateToURL(browser(), GetPageWithDefaultFavicon()));
+
+  base::test::TestFuture<FetchIconsFromDocumentResult> future;
+  DocumentIconFetcher::FetchIcons(
+      *browser()->tab_strip_model()->GetActiveWebContents(),
+      future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+  EXPECT_TRUE(future.Get().has_value());
+  std::vector<SkBitmap> images = future.Get().value();
+  SkBitmap expected_16;
+  ASSERT_OK_AND_ASSIGN(
+      expected_16, LoadImageFromTestFile(base::FilePath(FILE_PATH_LITERAL(
+                       "shortcuts/default_icon_has_two/16_favicon_part.png"))));
+  SkBitmap expected_32;
+  ASSERT_OK_AND_ASSIGN(
+      expected_32, LoadImageFromTestFile(base::FilePath(FILE_PATH_LITERAL(
+                       "shortcuts/default_icon_has_two/32_favicon_part.png"))));
+  EXPECT_THAT(images, testing::UnorderedElementsAre(
+                          gfx::test::EqualsBitmap(expected_16),
+                          gfx::test::EqualsBitmap(expected_32)));
+}
+
+IN_PROC_BROWSER_TEST_F(DocumentIconFetcherTest, WebContentsClosed) {
+  base::test::TestFuture<FetchIconsFromDocumentResult> future;
+  chrome::NewTab(browser());
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_https_test_server().GetURL(kPageWithIcons)));
+  DocumentIconFetcher::FetchIcons(
+      *browser()->tab_strip_model()->GetActiveWebContents(),
+      future.GetCallback());
+  chrome::CloseTab(browser());
+  ASSERT_TRUE(future.Wait());
+  EXPECT_THAT(
+      future.Get(),
+      base::test::ErrorIs(FetchIconsForDocumentError::kDocumentDestroyed));
+}
+
+}  // namespace
+}  // namespace shortcuts
diff --git a/chrome/browser/shortcuts/fetch_icons_from_document_task.cc b/chrome/browser/shortcuts/fetch_icons_from_document_task.cc
new file mode 100644
index 0000000..653a96e
--- /dev/null
+++ b/chrome/browser/shortcuts/fetch_icons_from_document_task.cc
@@ -0,0 +1,124 @@
+// 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/shortcuts/fetch_icons_from_document_task.h"
+
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/location.h"
+#include "base/memory/raw_ref.h"
+#include "base/types/expected.h"
+#include "components/webapps/common/web_page_metadata.mojom.h"
+#include "components/webapps/common/web_page_metadata_agent.mojom.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace shortcuts {
+
+FetchIconsFromDocumentTask::FetchIconsFromDocumentTask(
+    base::PassKey<DocumentIconFetcher>,
+    content::RenderFrameHost& rfh)
+    : frame_host_(rfh) {
+  CHECK(frame_host_->IsInPrimaryMainFrame());
+}
+
+FetchIconsFromDocumentTask::~FetchIconsFromDocumentTask() = default;
+
+void FetchIconsFromDocumentTask::Start(
+    FetchIconsFromDocumentCallback callback) {
+  callback_ = std::move(callback);
+  mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent> metadata_agent;
+  frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(&metadata_agent);
+
+  // Set the error handler so that we can run abort this task if the WebContents
+  // or the RenderFrameHost are destroyed and the connection to
+  // ChromeRenderFrame is lost.
+  metadata_agent.set_disconnect_handler(
+      base::BindOnce(&FetchIconsFromDocumentTask::OnMetadataFetchError,
+                     weak_factory_.GetWeakPtr()));
+
+  // Bind the InterfacePtr into the callback so that it's kept alive
+  // until there's either a connection error or a response.
+  auto* web_page_metadata_proxy = metadata_agent.get();
+  web_page_metadata_proxy->GetWebPageMetadata(
+      base::BindOnce(&FetchIconsFromDocumentTask::OnWebPageMetadataObtained,
+                     weak_factory_.GetWeakPtr(), std::move(metadata_agent)));
+}
+
+FetchIconsFromDocumentCallback FetchIconsFromDocumentTask::TakeCallback() {
+  return std::move(callback_);
+}
+
+void FetchIconsFromDocumentTask::OnWebPageMetadataObtained(
+    mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent> metadata_agent,
+    webapps::mojom::WebPageMetadataPtr web_page_metadata) {
+  metadata_fetch_complete_ = true;
+  std::vector<GURL> icons;
+  for (const auto& icon_info : web_page_metadata->icons) {
+    icons.push_back(icon_info->url);
+  }
+  for (const auto& favicon_url : frame_host_->FaviconURLs()) {
+    icons.push_back(favicon_url->icon_url);
+  }
+
+  // Eliminate duplicates.
+  base::flat_set<GURL> icon_set(std::move(icons));
+  num_pending_image_requests_ = icon_set.size();
+
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(&frame_host_.get());
+  for (const GURL& url : icon_set) {
+    web_contents->DownloadImageInFrame(
+        frame_host_->GetGlobalId(), url, /*is_favicon=*/true,
+        /*preferred_size=*/gfx::Size(),
+        /*max_bitmap_size=*/0, /*bypass_cache=*/false,
+        base::BindOnce(&FetchIconsFromDocumentTask::DidDownloadFavicon,
+                       weak_factory_.GetWeakPtr()));
+  }
+  MaybeCompleteImageDownloadAndSelfDestruct();
+}
+
+void FetchIconsFromDocumentTask::DidDownloadFavicon(
+    int id,
+    int http_status_code,
+    const GURL& image_url,
+    const std::vector<SkBitmap>& bitmaps,
+    const std::vector<gfx::Size>& sizes) {
+  icons_.reserve(icons_.size() + bitmaps.size());
+  for (const SkBitmap& bitmap : bitmaps) {
+    if (bitmap.drawsNothing()) {
+      continue;
+    }
+    icons_.push_back(bitmap);
+  }
+  --num_pending_image_requests_;
+  MaybeCompleteImageDownloadAndSelfDestruct();
+}
+
+void FetchIconsFromDocumentTask::MaybeCompleteImageDownloadAndSelfDestruct() {
+  if (!metadata_fetch_complete_ || num_pending_image_requests_ > 0) {
+    return;
+  }
+  OnCompleteSelfDestruct(base::ok(std::move(icons_)));
+}
+
+void FetchIconsFromDocumentTask::OnCompleteSelfDestruct(Result result,
+                                                        base::Location here) {
+  if (!callback_) {
+    return;
+  }
+  std::move(callback_).Run(std::move(result));
+}
+
+void FetchIconsFromDocumentTask::OnMetadataFetchError() {
+  OnCompleteSelfDestruct(
+      base::unexpected(FetchIconsForDocumentError::kMetadataFetchFailed));
+}
+
+}  // namespace shortcuts
diff --git a/chrome/browser/shortcuts/fetch_icons_from_document_task.h b/chrome/browser/shortcuts/fetch_icons_from_document_task.h
new file mode 100644
index 0000000..7ef5b9a
--- /dev/null
+++ b/chrome/browser/shortcuts/fetch_icons_from_document_task.h
@@ -0,0 +1,101 @@
+// 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_SHORTCUTS_FETCH_ICONS_FROM_DOCUMENT_TASK_H_
+#define CHROME_BROWSER_SHORTCUTS_FETCH_ICONS_FROM_DOCUMENT_TASK_H_
+
+#include <vector>
+
+#include "base/functional/callback_forward.h"
+#include "base/memory/raw_ref.h"
+#include "base/memory/weak_ptr.h"
+#include "base/types/expected.h"
+#include "base/types/pass_key.h"
+#include "base/values.h"
+#include "components/webapps/common/web_page_metadata.mojom-forward.h"
+#include "components/webapps/common/web_page_metadata_agent.mojom-forward.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+
+class SkBitmap;
+class GURL;
+
+namespace base {
+class Location;
+}
+
+namespace content {
+class RenderFrameHost;
+}
+
+namespace gfx {
+class Size;
+}
+
+namespace shortcuts {
+class DocumentIconFetcher;
+
+enum class FetchIconsForDocumentError {
+  kDocumentDestroyed,
+  kMetadataFetchFailed
+};
+using FetchIconsFromDocumentResult =
+    base::expected<std::vector<SkBitmap>, FetchIconsForDocumentError>;
+using FetchIconsFromDocumentCallback =
+    base::OnceCallback<void(FetchIconsFromDocumentResult)>;
+
+// Fetch all icons for the current RenderFrameHost document.
+// Invariants:
+// - The `frame_host_` provided to this class must be alive for the lifetime of
+//   this class.
+// - The `callback` may be called synchronously.
+// - The `callback` will be NOT be called on destruction. To handle that, the
+// user
+//   of this class must use the `TakeCallback()` method to extract the callback.
+class FetchIconsFromDocumentTask {
+ public:
+  using Result = FetchIconsFromDocumentResult;
+
+  // `rfh` must be the primary main frame.
+  FetchIconsFromDocumentTask(base::PassKey<DocumentIconFetcher>,
+                             content::RenderFrameHost& rfh);
+  ~FetchIconsFromDocumentTask();
+
+  // The `callback` may be called synchronously.
+  void Start(FetchIconsFromDocumentCallback callback);
+
+  FetchIconsFromDocumentCallback TakeCallback();
+
+ private:
+  void OnWebPageMetadataObtained(
+      mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent>
+          metadata_agent,
+      webapps::mojom::WebPageMetadataPtr web_page_metadata);
+
+  void DownloadIcon(const GURL& url);
+
+  void DidDownloadFavicon(int id,
+                          int http_status_code,
+                          const GURL& image_url,
+                          const std::vector<SkBitmap>& bitmaps,
+                          const std::vector<gfx::Size>& sizes);
+
+  void MaybeCompleteImageDownloadAndSelfDestruct();
+
+  void OnCompleteSelfDestruct(Result result, base::Location here = FROM_HERE);
+
+  void OnMetadataFetchError();
+
+  base::raw_ref<content::RenderFrameHost> frame_host_;
+  FetchIconsFromDocumentCallback callback_;
+
+  bool metadata_fetch_complete_ = false;
+  int num_pending_image_requests_ = 0;
+  std::vector<SkBitmap> icons_;
+
+  base::WeakPtrFactory<FetchIconsFromDocumentTask> weak_factory_{this};
+};
+
+}  // namespace shortcuts
+
+#endif  // CHROME_BROWSER_SHORTCUTS_FETCH_ICONS_FROM_DOCUMENT_TASK_H_
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
index 5946190..3627767 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
@@ -40,6 +40,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/signin/dice_web_signin_interceptor_delegate.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -301,6 +302,8 @@
         Profile::FromBrowserContext(context), std::move(fake_delegate));
   }
 
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
+
   std::map<content::BrowserContext*, FakeDiceWebSigninInterceptorDelegate*>
       interceptor_delegates_;
 };
diff --git a/chrome/browser/site_isolation/DEPS b/chrome/browser/site_isolation/DEPS
deleted file mode 100644
index 7d6f2785..0000000
--- a/chrome/browser/site_isolation/DEPS
+++ /dev/null
@@ -1,8 +0,0 @@
-specific_include_rules = {
-  "chrome_site_per_process_browsertest.cc": [
-    "+pdf/pdf_features.h",
-  ],
-  "site_per_process_interactive_browsertest.cc": [
-    "+pdf/pdf_features.h",
-  ],
-}
\ No newline at end of file
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index f28fceb1..334648c 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -68,6 +68,7 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/common/chrome_features.h"
@@ -715,22 +716,6 @@
         browser(), https_server_.GetURL(replacement_path)));
   }
 
-  Browser* InstallAndOpenTestWebApp(const GURL& start_url) {
-    auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
-    web_app_info->start_url = start_url;
-    web_app_info->scope = start_url.GetWithoutFilename();
-    web_app_info->title = u"Test app";
-    web_app_info->description = u"Test description";
-
-    Profile* profile = browser()->profile();
-
-    webapps::AppId app_id =
-        web_app::test::InstallWebApp(profile, std::move(web_app_info));
-
-    Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id);
-    return app_browser;
-  }
-
   void UpdateChromePolicy(const policy::PolicyMap& policies) {
     policy_provider_.UpdateChromePolicy(policies);
     ASSERT_TRUE(base::CurrentThread::Get());
@@ -1300,9 +1285,31 @@
       browser()->tab_strip_model()->delegate()->ShouldDisplayFavicon(tab));
 }
 
+class SSLUITestWithWebApps : public SSLUITest {
+ public:
+  Browser* InstallAndOpenTestWebApp(const GURL& start_url) {
+    auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
+    web_app_info->start_url = start_url;
+    web_app_info->scope = start_url.GetWithoutFilename();
+    web_app_info->title = u"Test app";
+    web_app_info->description = u"Test description";
+
+    Profile* profile = browser()->profile();
+
+    webapps::AppId app_id =
+        web_app::test::InstallWebApp(profile, std::move(web_app_info));
+
+    Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id);
+    return app_browser;
+  }
+
+ private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
+};
+
 // Visits a page in an app window with https error and proceed:
 // Disabled due to flaky failures; see https://crbug.com/1156046.
-IN_PROC_BROWSER_TEST_F(SSLUITest,
+IN_PROC_BROWSER_TEST_F(SSLUITestWithWebApps,
                        DISABLED_InAppTestHTTPSExpiredCertAndProceed) {
   ASSERT_TRUE(https_server_expired_.Start());
 
@@ -1318,7 +1325,7 @@
 }
 
 // Visits a page with https error and proceed. Then open the app and proceed.
-IN_PROC_BROWSER_TEST_F(SSLUITest,
+IN_PROC_BROWSER_TEST_F(SSLUITestWithWebApps,
                        InAppTestHTTPSExpiredCertAndPreviouslyProceeded) {
   ASSERT_TRUE(https_server_expired_.Start());
 
diff --git a/chrome/browser/ssl/ssl_fenced_frame_browsertest.cc b/chrome/browser/ssl/ssl_fenced_frame_browsertest.cc
index dedc290..695baab 100644
--- a/chrome/browser/ssl/ssl_fenced_frame_browsertest.cc
+++ b/chrome/browser/ssl/ssl_fenced_frame_browsertest.cc
@@ -6,6 +6,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -68,7 +69,7 @@
     Browser* app_browser = web_app::LaunchWebAppBrowserAndWait(profile, app_id);
     return app_browser;
   }
-
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   content::test::FencedFrameTestHelper fenced_frame_helper_;
 };
 
diff --git a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java
index d2209c3..a17ec44 100644
--- a/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java
+++ b/chrome/browser/tab_group/java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilter.java
@@ -382,22 +382,6 @@
             if (didCreateNewGroup && isTabInTabGroup(tab)) {
                 didCreateNewGroup = false;
             }
-        }
-
-        // This specific observer emission must happen before the below code which moves the
-        // tabs on grouping. The reason this must happen before is because moving a tab will
-        // update the tab group's favicon color in TabListMediator and cause it to generate a
-        // tab group color. This may indicate an improper state where tab groups that already
-        // have a color are not considered new groups, and the color should be set during the
-        // TabGroupCreationManager#showDialog flow anyways.
-        for (TabGroupModelFilterObserver observer : mGroupFilterObserver) {
-            if (didCreateNewGroup) {
-                observer.didCreateNewGroup(destinationTab, this);
-            }
-        }
-
-        for (int i = 0; i < tabs.size(); i++) {
-            Tab tab = tabs.get(i);
 
             // When merging tabs are in the same group, only make one willMergeTabToGroup call.
             if (!isSameGroup || i == tabs.size() - 1) {
@@ -444,6 +428,10 @@
         }
 
         for (TabGroupModelFilterObserver observer : mGroupFilterObserver) {
+            if (didCreateNewGroup) {
+                observer.didCreateNewGroup(destinationTab, this);
+            }
+
             // If this is a new tab group creation, do not trigger a snackbar.
             boolean skipSnackbarForCreation =
                     didCreateNewGroup && ChromeFeatureList.sTabGroupParityAndroid.isEnabled();
diff --git a/chrome/browser/ui/DEPS b/chrome/browser/ui/DEPS
index 774c66c..51a2d29 100644
--- a/chrome/browser/ui/DEPS
+++ b/chrome/browser/ui/DEPS
@@ -29,7 +29,6 @@
   "+components/user_education/webui",
   "+components/user_notes",
   "+components/web_package",
-  "+pdf/pdf_features.h",
   "+services/content/public",
   "+services/device/public/mojom",
   "+services/screen_ai",
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index 4f49d40f..bd63ea69 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
@@ -677,12 +678,23 @@
 }
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
 
+class PopupBlockerBrowserTestWithWebApps : public PopupBlockerBrowserTest {
+ private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
+};
+
 // Reentrancy regression test for PopunderPreventer attempting to activate a
 // fullscreen web app window that is being closed; see crbug.com/331095620.
-IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
-                       CloseFullscreenStandaloneWebApp) {
+// TODO(crbug.com/335493696): Mac shims don't work with faked fullscreen.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_CloseFullscreenStandaloneWebApp \
+  DISABLED_CloseFullscreenStandaloneWebApp
+#else
+#define MAYBE_CloseFullscreenStandaloneWebApp CloseFullscreenStandaloneWebApp
+#endif
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTestWithWebApps,
+                       MAYBE_CloseFullscreenStandaloneWebApp) {
   GURL url = embedded_test_server()->GetURL("/web_apps/basic.html");
-  web_app::OsIntegrationManager::ScopedSuppressForTesting suppress;
   webapps::AppId id = web_app::InstallWebAppFromPage(browser(), url);
   Browser* app = web_app::LaunchWebAppBrowserAndWait(browser()->profile(), id);
   WebContents* tab = app->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 03620f99..800e7d4 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -2117,15 +2117,14 @@
 }
 
 bool Browser::CanEnterFullscreenModeForTab(
-    content::RenderFrameHost* requesting_frame,
-    const blink::mojom::FullscreenOptions& options) {
+    content::RenderFrameHost* requesting_frame) {
   // If the tab strip isn't editable then a drag session is in progress, and it
   // is not safe to enter fullscreen. https://crbug.com/1315080
   if (!tab_strip_model_delegate_->IsTabStripEditable())
     return false;
 
   return exclusive_access_manager_->fullscreen_controller()
-      ->CanEnterFullscreenModeForTab(requesting_frame, options.display_id);
+      ->CanEnterFullscreenModeForTab(requesting_frame);
 }
 
 void Browser::EnterFullscreenModeForTab(
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 6a3a4af..1c848af 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -1011,8 +1011,7 @@
   void RestoreFromWebAPI() override;
   ui::WindowShowState GetWindowShowState() const override;
   bool CanEnterFullscreenModeForTab(
-      content::RenderFrameHost* requesting_frame,
-      const blink::mojom::FullscreenOptions& options) override;
+      content::RenderFrameHost* requesting_frame) override;
   void EnterFullscreenModeForTab(
       content::RenderFrameHost* requesting_frame,
       const blink::mojom::FullscreenOptions& options) override;
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index 5023e56c..3c6fb66c 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -78,6 +78,7 @@
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -394,6 +395,9 @@
               OnBrowserCloseCancelled,
               (Browser * browser, BrowserClosingStatus reason),
               (override));
+
+ private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 // Launch the app on a page with no title, check that the app title was set
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index a9ebb52c..f6f1957 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1122,8 +1122,8 @@
       }
       DefaultBrowserPromptManager::UpdatePrefsForDismissedPrompt(
           browser_->profile());
-      DefaultBrowserPromptManager::GetInstance()->CloseAllPrompts();
-
+      DefaultBrowserPromptManager::GetInstance()->CloseAllPrompts(
+          DefaultBrowserPromptManager::CloseReason::kAccept);
       break;
 #endif
     default:
@@ -1325,6 +1325,7 @@
   command_updater_.UpdateCommandEnabled(IDC_SHOW_BETA_FORUM, true);
   command_updater_.UpdateCommandEnabled(
       IDC_BOOKMARKS_MENU, (!guest_session && !profile()->IsSystemProfile()));
+  command_updater_.UpdateCommandEnabled(IDC_SAVED_TAB_GROUPS_MENU, true);
   command_updater_.UpdateCommandEnabled(
       IDC_RECENT_TABS_MENU, (!guest_session && !profile()->IsSystemProfile() &&
                              !profile()->IsIncognitoProfile()));
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
index 81a0f64..06a703e 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
@@ -221,8 +221,7 @@
 }
 
 bool FullscreenController::CanEnterFullscreenModeForTab(
-    content::RenderFrameHost* requesting_frame,
-    const int64_t display_id) {
+    content::RenderFrameHost* requesting_frame) {
   DCHECK(requesting_frame);
   auto* web_contents = WebContents::FromRenderFrameHost(requesting_frame);
   DCHECK(web_contents);
@@ -244,7 +243,7 @@
   // could cause requestFullscreen promises to hang. If we are in this function,
   // the renderer expects a visual property update to call
   // |blink::FullscreenController::DidEnterFullscreen| to resolve promises.
-  DCHECK(CanEnterFullscreenModeForTab(requesting_frame, display_id));
+  DCHECK(CanEnterFullscreenModeForTab(requesting_frame));
   auto* web_contents = WebContents::FromRenderFrameHost(requesting_frame);
   DCHECK(web_contents);
 
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.h b/chrome/browser/ui/exclusive_access/fullscreen_controller.h
index 4b30e9a..643d434 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.h
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.h
@@ -114,9 +114,7 @@
 
   // Returns whether entering fullscreen with |EnterFullscreenModeForTab()| is
   // allowed.
-  bool CanEnterFullscreenModeForTab(
-      content::RenderFrameHost* requesting_frame,
-      const int64_t display_id = display::kInvalidDisplayId);
+  bool CanEnterFullscreenModeForTab(content::RenderFrameHost* requesting_frame);
 
   // Enter tab-initiated fullscreen mode. FullscreenController decides whether
   // to also fullscreen the browser window. See 'FullscreenWithinTab Note'.
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 8c5c9b50..52ac544 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -45,6 +45,7 @@
 #include "chrome/browser/web_applications/external_install_options.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
@@ -353,7 +354,7 @@
   // used by the NetworkService.
   content::ContentMockCertVerifier cert_verifier_;
 
-  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 // Tests that "Open link in new tab" opens a link in a foreground tab.
@@ -1356,7 +1357,13 @@
 // This test was flaky on Win7 because it was bumping up against a 45 second
 // timeout. If it starts flaking on Windows 10+, it should be broken up into
 // smaller tests. See https://crbug.com/807471.
-IN_PROC_BROWSER_TEST_P(HostedAppProcessModelTest, FromOutsideHostedApp) {
+// TODO(crbug.com/335469702): Flaky on Linux ChromiumOS MSAN.
+#if BUILDFLAG(IS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_FromOutsideHostedApp DISABLED_FromOutsideHostedApp
+#else
+#define MAYBE_FromOutsideHostedApp FromOutsideHostedApp
+#endif
+IN_PROC_BROWSER_TEST_P(HostedAppProcessModelTest, MAYBE_FromOutsideHostedApp) {
   // Set up and launch the hosted app.
   GURL app_url =
       embedded_test_server()->GetURL("app.site.test", "/frame_tree/simple.htm");
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
index 64b8ccb..c458ae0 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -333,8 +333,14 @@
             SidePanelEntry::Id::kLensOverlayResults);
 }
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS) && !BUILDFLAG(IS_CHROMEOS_DEVICE) && \
+    defined(MEMORY_SANTIZER)
+#define MAYBE_DelayPermissionsPrompt DISABLED_DelayPermissionsPrompt
+#else
+#define MAYBE_DelayPermissionsPrompt DelayPermissionsPrompt
+#endif
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
-                       DelayPermissionsPrompt) {
+                       MAYBE_DelayPermissionsPrompt) {
   // Navigate to a page so we can request permissions
   WaitForPaint();
 
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service.cc b/chrome/browser/ui/safety_hub/menu_notification_service.cc
index ec1baf4..5ce71e5 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_service.cc
@@ -50,6 +50,21 @@
   const base::Value::Dict& stored_notifications =
       pref_service_->GetDict(safety_hub_prefs::kMenuNotificationsPrefsKey);
 
+  pref_dict_key_map_ = {
+      {safety_hub::SafetyHubModuleType::UNUSED_SITE_PERMISSIONS,
+       "unused-site-permissions"},
+      {safety_hub::SafetyHubModuleType::NOTIFICATION_PERMISSIONS,
+       "notification-permissions"},
+      {safety_hub::SafetyHubModuleType::SAFE_BROWSING, "safe-browsing"},
+      {safety_hub::SafetyHubModuleType::EXTENSIONS, "extensions"},
+  };
+  // PasswordStatusCheckService might be null for some profiles and testing. Add
+  // to the dictionary only if the service is available.
+  if (password_check_service) {
+    pref_dict_key_map_.emplace(safety_hub::SafetyHubModuleType::PASSWORDS,
+                               "passwords");
+  }
+
   // TODO(crbug.com/1443466): Make the interval for each service finch
   // configurable.
   // The Safety Hub services will be available whenever the |GetCachedResult|
@@ -77,12 +92,16 @@
                                      base::Unretained(extension_info_service),
                                      profile, true),
                  stored_notifications);
-  SetInfoElement(
-      safety_hub::SafetyHubModuleType::PASSWORDS,
-      MenuNotificationPriority::HIGH, base::Days(0),
-      base::BindRepeating(&PasswordStatusCheckService::GetCachedResult,
-                          base::Unretained(password_check_service)),
-      stored_notifications);
+  // PasswordStatusCheckService might be null for some profiles and testing. Add
+  // the info item only if the service is available.
+  if (password_check_service) {
+    SetInfoElement(
+        safety_hub::SafetyHubModuleType::PASSWORDS,
+        MenuNotificationPriority::HIGH, base::Days(0),
+        base::BindRepeating(&PasswordStatusCheckService::GetCachedResult,
+                            base::Unretained(password_check_service)),
+        stored_notifications);
+  }
 
   // Listen for changes to the Safe Browsing pref to accommodate the trigger
   // logic.
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service.h b/chrome/browser/ui/safety_hub/menu_notification_service.h
index aca05b6..d297bbdf 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service.h
+++ b/chrome/browser/ui/safety_hub/menu_notification_service.h
@@ -132,16 +132,8 @@
   // Called when the pref for Safe Browsing has been updated.
   void OnSafeBrowsingPrefUpdate();
 
-  const std::map<safety_hub::SafetyHubModuleType, const char*>
-      pref_dict_key_map_ = {
-          {safety_hub::SafetyHubModuleType::UNUSED_SITE_PERMISSIONS,
-           "unused-site-permissions"},
-          {safety_hub::SafetyHubModuleType::NOTIFICATION_PERMISSIONS,
-           "notification-permissions"},
-          {safety_hub::SafetyHubModuleType::SAFE_BROWSING, "safe-browsing"},
-          {safety_hub::SafetyHubModuleType::EXTENSIONS, "extensions"},
-          {safety_hub::SafetyHubModuleType::PASSWORDS, "passwords"},
-      };
+  // Holds the mapping from module type to pref name.
+  std::map<safety_hub::SafetyHubModuleType, const char*> pref_dict_key_map_;
 
   // Preference service that persists the notifications.
   raw_ptr<PrefService> pref_service_;
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service_factory.cc b/chrome/browser/ui/safety_hub/menu_notification_service_factory.cc
index f274a905..f04b25b 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service_factory.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_service_factory.cc
@@ -40,6 +40,8 @@
               .WithRegular(ProfileSelection::kOriginalOnly)
               .Build()) {
   DependsOn(UnusedSitePermissionsServiceFactory::GetInstance());
+  DependsOn(NotificationPermissionsReviewServiceFactory::GetInstance());
+  DependsOn(PasswordStatusCheckServiceFactory::GetInstance());
   DependsOn(extensions::ExtensionPrefsFactory::GetInstance());
 }
 
diff --git a/chrome/browser/ui/safety_hub/notification_permission_review_service_factory.cc b/chrome/browser/ui/safety_hub/notification_permission_review_service_factory.cc
index 34e8c3a..e71e0c6 100644
--- a/chrome/browser/ui/safety_hub/notification_permission_review_service_factory.cc
+++ b/chrome/browser/ui/safety_hub/notification_permission_review_service_factory.cc
@@ -50,7 +50,3 @@
       engagement_service);
 }
 
-bool NotificationPermissionsReviewServiceFactory::
-    ServiceIsCreatedWithBrowserContext() const {
-  return base::FeatureList::IsEnabled(features::kSafetyHub);
-}
diff --git a/chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h b/chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h
index 7e75491..9c38adc 100644
--- a/chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h
+++ b/chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h
@@ -41,8 +41,6 @@
   // BrowserContextKeyedServiceFactory:
   std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
       content::BrowserContext* context) const override;
-
-  bool ServiceIsCreatedWithBrowserContext() const override;
 };
 
 #endif  // CHROME_BROWSER_UI_SAFETY_HUB_NOTIFICATION_PERMISSION_REVIEW_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/safety_hub/notification_permission_review_service_unittest.cc b/chrome/browser/ui/safety_hub/notification_permission_review_service_unittest.cc
index 99655ac4..e2a7efe7 100644
--- a/chrome/browser/ui/safety_hub/notification_permission_review_service_unittest.cc
+++ b/chrome/browser/ui/safety_hub/notification_permission_review_service_unittest.cc
@@ -249,6 +249,8 @@
       NotificationsEngagementServiceFactory::GetForProfile(profile());
   notification_engagement_service->RecordNotificationDisplayed(
       GURL("https://google.com:443"), 10 * 7);
+  // Letting service update the cached result.
+  safety_hub_test_util::UpdateSafetyHubServiceAsync(service);
   const auto& updated_notification_permissions =
       service->PopulateNotificationPermissionReviewData();
   EXPECT_EQ(2UL, updated_notification_permissions.size());
diff --git a/chrome/browser/ui/safety_hub/password_status_check_service.cc b/chrome/browser/ui/safety_hub/password_status_check_service.cc
index 4cd0b7d..00a65b9 100644
--- a/chrome/browser/ui/safety_hub/password_status_check_service.cc
+++ b/chrome/browser/ui/safety_hub/password_status_check_service.cc
@@ -285,14 +285,9 @@
           profile_, ServiceAccessType::IMPLICIT_ACCESS);
 
   // ProfilePasswordStore may not exist for some cases like non-user profiles on
-  // Ash. If ProfilePasswordStore does not exist, then no other password
-  // operations can be done. Hence, here is an early return to prevent
-  // initializing checks.
-  // TODO(crbug.com/1443466): Add CHECK for profile_store after making this
-  // service null if the store does not exist.
-  if (!profile_store) {
-    return;
-  }
+  // Ash. If ProfilePasswordStore does not exist, the service should not be
+  // created by the factory.
+  DCHECK(profile_store);
 
   profile_password_store_observation_.Observe(profile_store.get());
   if (account_store) {
diff --git a/chrome/browser/ui/safety_hub/password_status_check_service_factory.cc b/chrome/browser/ui/safety_hub/password_status_check_service_factory.cc
index 10fc12fd..d3a3233 100644
--- a/chrome/browser/ui/safety_hub/password_status_check_service_factory.cc
+++ b/chrome/browser/ui/safety_hub/password_status_check_service_factory.cc
@@ -52,11 +52,19 @@
   if (!base::FeatureList::IsEnabled(features::kSafetyHub)) {
     return nullptr;
   }
+
+  Profile* profile = Profile::FromBrowserContext(context);
+  password_manager::PasswordStoreInterface* store =
+      ProfilePasswordStoreFactory::GetForProfile(
+          profile, ServiceAccessType::EXPLICIT_ACCESS)
+          .get();
+  // Incognito, guest, or system profiles doesn't have PasswordStore so
+  // SafetyHub shouldn't be created as well.
+  if (!store) {
+    return nullptr;
+  }
+
   return std::make_unique<PasswordStatusCheckService>(
       Profile::FromBrowserContext(context));
 }
 
-bool PasswordStatusCheckServiceFactory::ServiceIsCreatedWithBrowserContext()
-    const {
-  return base::FeatureList::IsEnabled(features::kSafetyHub);
-}
diff --git a/chrome/browser/ui/safety_hub/password_status_check_service_factory.h b/chrome/browser/ui/safety_hub/password_status_check_service_factory.h
index 491ffc1d..082ba2d 100644
--- a/chrome/browser/ui/safety_hub/password_status_check_service_factory.h
+++ b/chrome/browser/ui/safety_hub/password_status_check_service_factory.h
@@ -34,8 +34,6 @@
   ~PasswordStatusCheckServiceFactory() override;
 
   // BrowserContextKeyedServiceFactory:
-  bool ServiceIsCreatedWithBrowserContext() const override;
-
   std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
       content::BrowserContext* context) const override;
 };
diff --git a/chrome/browser/ui/safety_hub/password_status_check_service_unittest.cc b/chrome/browser/ui/safety_hub/password_status_check_service_unittest.cc
index aabe4c9..935069ba6 100644
--- a/chrome/browser/ui/safety_hub/password_status_check_service_unittest.cc
+++ b/chrome/browser/ui/safety_hub/password_status_check_service_unittest.cc
@@ -252,25 +252,6 @@
   }
 };
 
-class PasswordStatusCheckServiceWithoutPasswordStoreTest
-    : public testing::Test {
- public:
-  PasswordStatusCheckService* service() { return service_.get(); }
-
-  content::BrowserTaskEnvironment* task_environment() { return &task_env_; }
-
- private:
-  void SetUp() override {
-    service_ = std::make_unique<PasswordStatusCheckService>(&profile_);
-    task_env_.RunUntilIdle();
-  }
-
-  content::BrowserTaskEnvironment task_env_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-  TestingProfile profile_;
-  std::unique_ptr<PasswordStatusCheckService> service_;
-};
-
 class PasswordStatusCheckServiceParameterizedSchedulingTest
     : public PasswordStatusCheckServiceBaseTest,
       public testing::WithParamInterface<int> {
@@ -868,20 +849,6 @@
               testing::ElementsAre(kOrigin1));
 }
 
-TEST_F(PasswordStatusCheckServiceWithoutPasswordStoreTest, NoPasswordStored) {
-  // Let the time pass until a check should have happened.
-  task_environment()->AdvanceClock(base::Days(30));
-  task_environment()->RunUntilIdle();
-
-  // Expect that nothing is initialized.
-  EXPECT_FALSE(service()->GetSavedPasswordsPresenterForTesting());
-  EXPECT_FALSE(service()->GetPasswordCheckDelegateForTesting());
-  EXPECT_FALSE(service()->IsObservingSavedPasswordsPresenterForTesting());
-  EXPECT_FALSE(service()->IsObservingBulkLeakCheckForTesting());
-  EXPECT_FALSE(service()->is_password_check_running());
-  EXPECT_FALSE(service()->is_update_credential_count_pending());
-}
-
 TEST_P(PasswordStatusCheckServiceParameterizedSchedulingTest,
        CheckWeightedRandomScheduling) {
   base::test::ScopedFeatureList feature_list;
diff --git a/chrome/browser/ui/safety_hub/safety_hub_test_util.cc b/chrome/browser/ui/safety_hub/safety_hub_test_util.cc
index 31cdf51c..3da17ad 100644
--- a/chrome/browser/ui/safety_hub/safety_hub_test_util.cc
+++ b/chrome/browser/ui/safety_hub/safety_hub_test_util.cc
@@ -8,6 +8,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/run_until.h"
+#include "chrome/browser/ui/safety_hub/password_status_check_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/crx_file/id_util.h"
 #include "extensions/browser/extension_prefs.h"
@@ -112,6 +113,14 @@
   }));
 }
 
+void RunUntilPasswordCheckCompleted(Profile* profile) {
+  PasswordStatusCheckService* service =
+      PasswordStatusCheckServiceFactory::GetForProfile(profile);
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    return !service->IsUpdateRunning() && !service->IsInfrastructureReady();
+  }));
+}
+
 std::unique_ptr<testing::NiceMock<MockCWSInfoService>> GetMockCWSInfoService(
     Profile* profile,
     bool with_calls) {
diff --git a/chrome/browser/ui/safety_hub/safety_hub_test_util.h b/chrome/browser/ui/safety_hub/safety_hub_test_util.h
index 7f3180c..ffee56b 100644
--- a/chrome/browser/ui/safety_hub/safety_hub_test_util.h
+++ b/chrome/browser/ui/safety_hub/safety_hub_test_util.h
@@ -37,6 +37,10 @@
 void UpdatePasswordCheckServiceAsync(
     PasswordStatusCheckService* password_service);
 
+// This will run until ongoing checks in PasswordStatusCheckService to be
+// completed.
+void RunUntilPasswordCheckCompleted(Profile* profile);
+
 // Creates a mock service that returns mock results for the CWS info service. If
 // |with_calls| is true, total six extensions with different properties are
 // mocked: malware, policy violation, unpublished, combination of malware and
diff --git a/chrome/browser/ui/startup/default_browser_prompt_manager.cc b/chrome/browser/ui/startup/default_browser_prompt_manager.cc
index 592b62e..7594907c 100644
--- a/chrome/browser/ui/startup/default_browser_prompt_manager.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt_manager.cc
@@ -100,12 +100,14 @@
   }
 }
 
-void DefaultBrowserPromptManager::CloseAllPrompts() {
+void DefaultBrowserPromptManager::CloseAllPrompts(CloseReason close_reason) {
   CloseAllInfoBars();
 
   SetShowAppMenuPromptVisibility(false);
 
-  SetAppMenuItemVisibility(false);
+  if (close_reason == CloseReason::kAccept) {
+    SetAppMenuItemVisibility(false);
+  }
 }
 
 bool DefaultBrowserPromptManager::ShouldTrackBrowser(Browser* browser) {
@@ -141,9 +143,9 @@
   static_cast<ConfirmInfoBarDelegate*>(infobar->delegate())
       ->RemoveObserver(this);
 
-  if (user_initiated_info_bar_close_pending_) {
-    CloseAllPrompts();
-    user_initiated_info_bar_close_pending_ = false;
+  if (user_initiated_info_bar_close_pending_.has_value()) {
+    CloseAllPrompts(user_initiated_info_bar_close_pending_.value());
+    user_initiated_info_bar_close_pending_.reset();
   }
 }
 
@@ -152,11 +154,11 @@
                               g_browser_process->local_state()->GetInteger(
                                   prefs::kDefaultBrowserDeclinedCount) +
                                   1);
-  user_initiated_info_bar_close_pending_ = true;
+  user_initiated_info_bar_close_pending_ = CloseReason::kAccept;
 }
 
 void DefaultBrowserPromptManager::OnDismiss() {
-  user_initiated_info_bar_close_pending_ = true;
+  user_initiated_info_bar_close_pending_ = CloseReason::kDismiss;
 }
 
 DefaultBrowserPromptManager::DefaultBrowserPromptManager() = default;
@@ -264,7 +266,8 @@
         FROM_HERE, app_menu_remaining_duration, base::BindOnce([]() {
           UpdatePrefsForDismissedPrompt(
               BrowserList::GetInstance()->GetLastActive()->profile());
-          DefaultBrowserPromptManager::GetInstance()->CloseAllPrompts();
+          DefaultBrowserPromptManager::GetInstance()->CloseAllPrompts(
+              CloseReason::kDismiss);
         }));
   } else {
     app_menu_prompt_dismiss_timer_.Stop();
diff --git a/chrome/browser/ui/startup/default_browser_prompt_manager.h b/chrome/browser/ui/startup/default_browser_prompt_manager.h
index a4ba8e1..95f0fc9 100644
--- a/chrome/browser/ui/startup/default_browser_prompt_manager.h
+++ b/chrome/browser/ui/startup/default_browser_prompt_manager.h
@@ -35,6 +35,11 @@
     virtual void OnShowAppMenuPromptChanged() = 0;
   };
 
+  enum class CloseReason {
+    kAccept,
+    kDismiss,
+  };
+
   static DefaultBrowserPromptManager* GetInstance();
 
   // Resets the tracking preferences for the default browser prompts so that
@@ -59,7 +64,8 @@
   void RemoveObserver(Observer* observer);
 
   void MaybeShowPrompt();
-  void CloseAllPrompts();
+
+  void CloseAllPrompts(CloseReason close_reason);
 
   // BrowserTabStripTrackerDelegate
   bool ShouldTrackBrowser(Browser* browser) override;
@@ -100,7 +106,8 @@
 
   std::unique_ptr<BrowserTabStripTracker> browser_tab_strip_tracker_;
   std::map<content::WebContents*, infobars::InfoBar*> infobars_;
-  bool user_initiated_info_bar_close_pending_ = false;
+
+  std::optional<CloseReason> user_initiated_info_bar_close_pending_;
 
   bool show_app_menu_prompt_ = false;
   bool show_app_menu_item_ = false;
diff --git a/chrome/browser/ui/startup/default_browser_prompt_manager_unittest.cc b/chrome/browser/ui/startup/default_browser_prompt_manager_unittest.cc
index 90193c2..a95b497e0 100644
--- a/chrome/browser/ui/startup/default_browser_prompt_manager_unittest.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt_manager_unittest.cc
@@ -44,7 +44,8 @@
     BrowserWithTestWindowTest::SetUp();
 
     manager_ = DefaultBrowserPromptManager::GetInstance();
-    manager_->CloseAllPrompts();
+    manager_->CloseAllPrompts(
+        DefaultBrowserPromptManager::CloseReason::kAccept);
 
     // Set up a single tab in the foreground.
     std::unique_ptr<content::WebContents> contents =
@@ -82,7 +83,8 @@
       local_state()->ClearPref(prefs::kDefaultBrowserDeclinedCount);
     }
 
-    manager()->CloseAllPrompts();
+    manager()->CloseAllPrompts(
+        DefaultBrowserPromptManager::CloseReason::kAccept);
 
     infobars::ContentInfoBarManager* infobar_manager =
         infobars::ContentInfoBarManager::FromWebContents(
@@ -157,6 +159,30 @@
   ASSERT_FALSE(manager->get_show_app_menu_item());
 }
 
+TEST_F(DefaultBrowserPromptManagerTest, AppMenuItemHiddenOnPromptAccept) {
+  EnableDefaultBrowserPromptRefreshFeatureWithParams(
+      {{features::kShowDefaultBrowserAppMenuItem.name, "true"}});
+
+  auto* manager = DefaultBrowserPromptManager::GetInstance();
+  manager->MaybeShowPrompt();
+  ASSERT_TRUE(manager->get_show_app_menu_item());
+
+  manager->CloseAllPrompts(DefaultBrowserPromptManager::CloseReason::kAccept);
+  ASSERT_FALSE(manager->get_show_app_menu_item());
+}
+
+TEST_F(DefaultBrowserPromptManagerTest, AppMenuItemPersistsOnPromptDismissed) {
+  EnableDefaultBrowserPromptRefreshFeatureWithParams(
+      {{features::kShowDefaultBrowserAppMenuItem.name, "true"}});
+
+  auto* manager = DefaultBrowserPromptManager::GetInstance();
+  manager->MaybeShowPrompt();
+  ASSERT_TRUE(manager->get_show_app_menu_item());
+
+  manager->CloseAllPrompts(DefaultBrowserPromptManager::CloseReason::kDismiss);
+  ASSERT_TRUE(manager->get_show_app_menu_item());
+}
+
 TEST_F(DefaultBrowserPromptManagerTest, InfoBarMaxPromptCount) {
   // If max prompt count is negative, do not limit the number of times the
   // prompt is shown.
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index bfaf6abd..443c59e2 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_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/startup/startup_browser_creator.h"
+
 #include <stddef.h>
 
 #include <algorithm>
@@ -69,7 +71,6 @@
 #include "chrome/browser/ui/profiles/profile_ui_test_utils.h"
 #include "chrome/browser/ui/search/ntp_test_utils.h"
 #include "chrome/browser/ui/startup/launch_mode_recorder.h"
-#include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
 #include "chrome/browser/ui/startup/startup_tab_provider.h"
 #include "chrome/browser/ui/startup/startup_types.h"
@@ -79,6 +80,7 @@
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_test_observers.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
@@ -1800,6 +1802,7 @@
   }
 
  private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
@@ -2060,6 +2063,7 @@
   bool browser_added_check_passed_ = false;
 
  private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   std::unique_ptr<
       base::MockCallback<upgrade_util::RelaunchChromeBrowserCallback>>
       mock_relaunch_callback_;
@@ -2188,7 +2192,7 @@
   WebAppProvider& provider() { return *WebAppProvider::GetForTest(profile()); }
 
   base::test::ScopedFeatureList scoped_feature_list_;
-  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_supress_;
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 IN_PROC_BROWSER_TEST_F(StartupBrowserWithWebAppTest,
@@ -2260,25 +2264,27 @@
 
 IN_PROC_BROWSER_TEST_F(StartupBrowserWithWebAppTest,
                        PRE_LastUsedProfilesWithWebApp) {
-  BrowserAddedObserver added_observer;
+  {
+    BrowserAddedObserver added_observer;
 
 #if BUILDFLAG(IS_MAC)
-  // Simulate an app shim connecting and launching an app.
-  apps::AppShimManager::Get()->LoadAndLaunchAppForTesting(kAppId);
+    // Simulate an app shim connecting and launching an app.
+    apps::AppShimManager::Get()->LoadAndLaunchAppForTesting(kAppId);
 #endif
 
-  content::RunAllTasksUntilIdle();
-  // Launching with an app opens the app window via a task, so the test
-  // might start before SelectFirstBrowser is called.
-  if (!browser()) {
-    added_observer.Wait();
-    SelectFirstBrowser();
+    content::RunAllTasksUntilIdle();
+    // Launching with an app opens the app window via a task, so the test
+    // might start before SelectFirstBrowser is called.
+    if (!browser()) {
+      added_observer.Wait();
+      SelectFirstBrowser();
+    }
   }
   ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
 
   // An app window should have been launched.
   EXPECT_TRUE(browser()->is_type_app());
-  CloseBrowserAsynchronously(browser());
+  CloseBrowserSynchronously(browser());
 }
 
 // TODO(crbug.com/327256043): Flaky on win
@@ -2445,6 +2451,9 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {}
 
   WebAppProvider& provider() { return *WebAppProvider::GetForTest(profile()); }
+
+ private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 IN_PROC_BROWSER_TEST_F(StartupBrowserWithRealWebAppTest,
@@ -2658,6 +2667,8 @@
         last_opened_profiles);
   }
 
+ private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::test::ScopedFeatureList scoped_feature_list_;
 #if BUILDFLAG(IS_WIN)
   // This is needed to stop StartupBrowserWebAppProtocolHandlingTests creating a
@@ -3525,7 +3536,7 @@
       policy_provider_.UpdateChromePolicy(policies);
     }
   }
-
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
 };
 
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc
index 75158e5ea..7b546ec 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.cc
@@ -114,7 +114,8 @@
 std::unique_ptr<ui::DialogModel>
 SavedTabGroupUtils::CreateSavedTabGroupContextMenuModel(
     Browser* browser,
-    const base::Uuid& saved_guid) {
+    const base::Uuid& saved_guid,
+    bool show_pin_unpin_option) {
   const auto* const service =
       SavedTabGroupServiceFactory::GetForProfile(browser->profile());
   const auto* const saved_group = service->model()->Get(saved_guid);
@@ -152,7 +153,7 @@
           .SetId(kMoveGroupToNewWindowMenuItem)
           .SetIsEnabled(should_enable_move_menu_item));
 
-  if (tab_groups::IsTabGroupsSaveUIUpdateEnabled()) {
+  if (tab_groups::IsTabGroupsSaveUIUpdateEnabled() && show_pin_unpin_option) {
     dialog_model.AddMenuItem(
         ui::ImageModel::FromVectorIcon(saved_group->is_pinned()
                                            ? kKeepPinFilledChromeRefreshIcon
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h
index 0692abd..decf5409 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h
@@ -58,9 +58,12 @@
   // Create the the context menu model for a saved tab group button or a saved
   // tab group menu item in the Everything menu. `browser` is the one from
   // which this method is invoked. `saved_guid` is the saved tab group's Uuid.
+  // Show a "Pin / Unpin group from bookmarks bar" option if
+  // `show_pin_unpin_option` is true.
   static std::unique_ptr<ui::DialogModel> CreateSavedTabGroupContextMenuModel(
       Browser* browser,
-      const base::Uuid& saved_guid);
+      const base::Uuid& saved_guid,
+      bool show_pin_unpin_option);
 
   // Converts a webcontents into a SavedTabGroupTab.
   static SavedTabGroupTab CreateSavedTabGroupTabFromWebContents(
diff --git a/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc b/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
index 658e00f7..a35db1e 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/prevent_close_test_base.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
@@ -93,6 +94,7 @@
               (override));
 
  protected:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::ScopedObservation<TabStripModel, TabStripModelPreventCloseTest>
       observer_{this};
 };
diff --git a/chrome/browser/ui/test/test_browser_ui.cc b/chrome/browser/ui/test/test_browser_ui.cc
index 40456e2..88dad994 100644
--- a/chrome/browser/ui/test/test_browser_ui.cc
+++ b/chrome/browser/ui/test/test_browser_ui.cc
@@ -32,6 +32,10 @@
 #include "ui/views/widget/widget.h"
 #endif
 
+#if BUILDFLAG(IS_WIN)
+#include "ui/events/platform/platform_event_source.h"
+#endif
+
 // TODO(crbug.com/40625383) support Mac for pixel tests.
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
@@ -64,10 +68,24 @@
         view->GetWidget()->GetNativeWindow()->GetRootWindow());
     generator.MoveMouseTo({0, 0});
     cursor_client_->DisableMouseEvents();
+#if BUILDFLAG(IS_WIN)
+    // On Windows, cursor client disable isn't consistently respected, and it's
+    // also used to handle touch -> mouse event translation, so use this
+    // instead. See crbug.com/333846475 for an example of the problem this
+    // solves.
+    ui::PlatformEventSource::SetIgnoreNativePlatformEvents(true);
+#endif
   }
+
   ScopedMouseDisabler(const ScopedMouseDisabler&) = delete;
   const ScopedMouseDisabler operator=(const ScopedMouseDisabler&) = delete;
-  ~ScopedMouseDisabler() { cursor_client_->EnableMouseEvents(); }
+
+  ~ScopedMouseDisabler() {
+#if BUILDFLAG(IS_WIN)
+    ui::PlatformEventSource::SetIgnoreNativePlatformEvents(false);
+#endif
+    cursor_client_->EnableMouseEvents();
+  }
 
  private:
   raw_ptr<aura::client::CursorClient> cursor_client_;
@@ -112,7 +130,7 @@
     return ui::test::ActionResult::kNotAttempted;
   }
 
-  // Disable and hide cursor to prvent any interference with the
+  // Disable and hide cursor to prevent any interference with the
   // screenshots.
   ScopedMouseDisabler disable(view);
 
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 9109cad..ba46618 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -95,6 +95,7 @@
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
 #include "components/profile_metrics/browser_profile_type.h"
+#include "components/saved_tab_groups/features.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
@@ -145,6 +146,7 @@
 using content::WebContents;
 
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kBookmarksMenuItem);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kTabGroupsMenuItem);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kDownloadsMenuItem);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kHistoryMenuItem);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kExtensionsMenuItem);
@@ -1732,6 +1734,18 @@
     SetElementIdentifierAt(GetIndexOfCommandId(IDC_BOOKMARKS_MENU).value(),
                            kBookmarksMenuItem);
   }
+
+  if (tab_groups::IsTabGroupsSaveUIUpdateEnabled() &&
+      browser_->profile()->IsRegularProfile()) {
+    auto saved_tab_groups_model = std::make_unique<ui::SimpleMenuModel>(this);
+    sub_menus_.push_back(std::move(saved_tab_groups_model));
+    AddSubMenuWithStringId(IDC_SAVED_TAB_GROUPS_MENU, IDS_SAVED_TAB_GROUPS_MENU,
+                           sub_menus_.back().get());
+    SetElementIdentifierAt(
+        GetIndexOfCommandId(IDC_SAVED_TAB_GROUPS_MENU).value(),
+        kTabGroupsMenuItem);
+  }
+
   WebContents* web_contents =
       browser_->tab_strip_model()->GetActiveWebContents();
   if (!browser_->profile()->IsOffTheRecord() && web_contents &&
@@ -1932,6 +1946,8 @@
     SetCommandIcon(this, IDC_RECENT_TABS_MENU, kHistoryIcon);
     SetCommandIcon(this, IDC_SHOW_DOWNLOADS, kDownloadMenuIcon);
     SetCommandIcon(this, IDC_BOOKMARKS_MENU, kBookmarksListsMenuIcon);
+    SetCommandIcon(this, IDC_SAVED_TAB_GROUPS_MENU,
+                   kSavedTabGroupBarEverythingIcon);
     SetCommandIcon(this, IDC_VIEW_PASSWORDS,
                    vector_icons::kPasswordManagerIcon);
     SetCommandIcon(this, IDC_ZOOM_MENU, kZoomInIcon);
diff --git a/chrome/browser/ui/toolbar/app_menu_model.h b/chrome/browser/ui/toolbar/app_menu_model.h
index c477fba..307c007 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.h
+++ b/chrome/browser/ui/toolbar/app_menu_model.h
@@ -164,6 +164,7 @@
                      public ui::ButtonMenuItemModel::Delegate {
  public:
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kBookmarksMenuItem);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kTabGroupsMenuItem);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kDownloadsMenuItem);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHistoryMenuItem);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kExtensionsMenuItem);
@@ -182,14 +183,15 @@
   // varies depending upon the underlying model. The command IDs for items in
   // these menus will be staggered and each increment by this value, so they
   // don't have conflicts. Currently, this accounts for the bookmarks, recent
-  // tabs menus, and the profile submenu.
-  static constexpr int kNumUnboundedMenuTypes = 3;
+  // tabs menus, the profile submenu and tab groups submenu.
+  static constexpr int kNumUnboundedMenuTypes = 4;
 
   // First command ID to use for each unbounded menu. These should be staggered,
   // and there should be kNumUnboundedMenuTypes of them.
   static constexpr int kMinBookmarksCommandId = IDC_FIRST_UNBOUNDED_MENU;
   static constexpr int kMinRecentTabsCommandId = kMinBookmarksCommandId + 1;
   static constexpr int kMinOtherProfileCommandId = kMinRecentTabsCommandId + 1;
+  static constexpr int kMinTabGroupsCommandId = kMinOtherProfileCommandId + 1;
 
   // Creates an app menu model for the given browser. Init() must be called
   // before passing this to an AppMenu. |app_menu_icon_controller|, if provided,
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 b31e533..38fc067 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
@@ -256,7 +256,7 @@
   }
 
   everything_menu_ = std::make_unique<STGEverythingMenu>(
-      overflow_button_->button_controller(), GetWidget(), browser_);
+      overflow_button_->button_controller(), browser_);
   everything_menu_->RunMenu();
 }
 
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
index 55f2520..391ae15 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
@@ -168,8 +168,7 @@
  public:
   void SetUp() override {
     SavedTabGroupBarUnitTest::SetUp();
-    everything_menu_ =
-        std::make_unique<STGEverythingMenu>(nullptr, nullptr, browser());
+    everything_menu_ = std::make_unique<STGEverythingMenu>(nullptr, browser());
   }
 
   void TearDown() override {
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
index 4e1e5ec..93c0bf9 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_button.cc
@@ -97,7 +97,8 @@
           base::BindRepeating(
               &SavedTabGroupUtils::CreateSavedTabGroupContextMenuModel,
               browser,
-              group.saved_guid()),
+              group.saved_guid(),
+              /*show_pin_unpin_option=*/true),
           views::MenuRunner::CONTEXT_MENU | views::MenuRunner::IS_NESTED) {
   SetAccessibilityProperties(
       ax::mojom::Role::kButton, /*name=*/GetAccessibleNameForButton(),
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.cc
index ab03707..654537c 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.cc
@@ -25,9 +25,25 @@
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(STGEverythingMenu, kTabGroup);
 
 STGEverythingMenu::STGEverythingMenu(views::MenuButtonController* controller,
-                                     views::Widget* widget,
                                      Browser* browser)
-    : menu_button_controller_(controller), browser_(browser), widget_(widget) {}
+    : menu_button_controller_(controller),
+      browser_(browser),
+      widget_(views::Widget::GetWidgetForNativeWindow(
+          browser->window()->GetNativeWindow())) {}
+
+int STGEverythingMenu::GetAndIncrementNextTabGroupItemID() {
+  const int current_id = next_tab_group_item_command_id_;
+  next_tab_group_item_command_id_ += AppMenuModel::kNumUnboundedMenuTypes;
+  return current_id;
+}
+
+const SavedTabGroup* STGEverythingMenu::GetTabGroupForCommandId(
+    int command_id) {
+  const int idx_in_sorted_tab_group =
+      (command_id - AppMenuModel::kMinTabGroupsCommandId) /
+      AppMenuModel::kNumUnboundedMenuTypes;
+  return sorted_tab_groups_[idx_in_sorted_tab_group];
+}
 
 const SavedTabGroupModel*
 STGEverythingMenu::GetSavedTabGroupModelFromBrowser() {
@@ -81,8 +97,9 @@
     const auto title = tab_group->title();
     // For saved tab group items, use the indice in `sorted_tab_groups_` as the
     // command ids.
+    const int command_id = GetAndIncrementNextTabGroupItemID();
     menu_model->AddItemWithIcon(
-        i /*command_id*/,
+        command_id,
         title.empty() ? l10n_util::GetPluralStringFUTF16(
                             IDS_SAVED_TAB_GROUP_TABS_COUNT,
                             static_cast<int>(tab_group->saved_tabs().size()))
@@ -91,18 +108,22 @@
     // Set the first tab group item with element id `kTabGroup`.
     if (i == 0) {
       menu_model->SetElementIdentifierAt(
-          menu_model->GetIndexOfCommandId(0).value(), kTabGroup);
+          menu_model->GetIndexOfCommandId(command_id).value(), kTabGroup);
     }
   }
   return menu_model;
 }
 
 void STGEverythingMenu::PopulateMenu(views::MenuItemView* parent) {
+  if (parent->HasSubmenu()) {
+    parent->GetSubmenu()->RemoveAllChildViews();
+  }
   model_ = CreateMenuModel();
   for (size_t i = 0, max = model_->GetItemCount(); i < max; ++i) {
     views::MenuModelAdapter::AppendMenuItemFromModel(model_.get(), i, parent,
                                                      model_->GetCommandIdAt(i));
   }
+  parent->GetSubmenu()->InvalidateLayout();
 }
 
 void STGEverythingMenu::RunMenu() {
@@ -120,7 +141,7 @@
   if (command_id == IDC_CREATE_NEW_TAB_GROUP) {
     browser_->command_controller()->ExecuteCommand(command_id);
   } else {
-    const auto* const group = sorted_tab_groups_[command_id];
+    const auto* const group = GetTabGroupForCommandId(command_id);
     if (group->saved_tabs().empty()) {
       return;
     }
@@ -138,13 +159,13 @@
     return false;
   }
 
-  const auto* const group = sorted_tab_groups_[command_id];
+  const auto* const group = GetTabGroupForCommandId(command_id);
   context_menu_controller_ =
       std::make_unique<views::DialogModelContextMenuController>(
           widget_->GetRootView(),
           base::BindRepeating(
               &SavedTabGroupUtils::CreateSavedTabGroupContextMenuModel,
-              browser_, group->saved_guid()),
+              browser_, group->saved_guid(), show_pin_unpin_option_),
           views::MenuRunner::CONTEXT_MENU | views::MenuRunner::IS_NESTED);
   context_menu_controller_->ShowContextMenuForViewImpl(widget_->GetRootView(),
                                                        p, source_type);
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.h b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.h
index 5cae5ff..78a22fd 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.h
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.h
@@ -6,11 +6,14 @@
 #define CHROME_BROWSER_UI_VIEWS_BOOKMARKS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_EVERYTHING_MENU_H_
 
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/toolbar/app_menu_model.h"
+#include "components/saved_tab_groups/features.h"
 #include "components/saved_tab_groups/saved_tab_group_model.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/views/controls/button/menu_button_controller.h"
 #include "ui/views/controls/menu/menu_delegate.h"
 #include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/dialog_model_context_menu_controller.h"
 
 namespace tab_groups {
@@ -25,7 +28,6 @@
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kTabGroup);
 
   STGEverythingMenu(views::MenuButtonController* menu_button_controller,
-                    views::Widget* widget,
                     Browser* browser);
 
   STGEverythingMenu(const STGEverythingMenu&) = delete;
@@ -43,9 +45,22 @@
 
   bool IsShowing() { return menu_runner_ && menu_runner_->IsRunning(); }
 
+  void SetShowPinOption(bool show_pin_unpin_option) {
+    show_pin_unpin_option_ = show_pin_unpin_option;
+  }
+
+  // override views::MenuDelegate:
+  void ExecuteCommand(int command_id, int event_flags) override;
+  bool ShowContextMenu(views::MenuItemView* source,
+                       int command_id,
+                       const gfx::Point& p,
+                       ui::MenuSourceType source_type) override;
+
  private:
   friend class STGEverythingMenuUnitTest;
 
+  int GetAndIncrementNextTabGroupItemID();
+  const SavedTabGroup* GetTabGroupForCommandId(int command_id);
   std::unique_ptr<ui::SimpleMenuModel> CreateMenuModel();
 
   // Returns sorted saved tab groups with the most recently created as the
@@ -55,24 +70,26 @@
 
   const SavedTabGroupModel* GetSavedTabGroupModelFromBrowser();
 
-  // override views::MenuDelegate:
-  void ExecuteCommand(int command_id, int event_flags) override;
-  bool ShowContextMenu(views::MenuItemView* source,
-                       int command_id,
-                       const gfx::Point& p,
-                       ui::MenuSourceType source_type) override;
-
   // Saved tab groups with the most recently created as the first.
   std::vector<const SavedTabGroup*> sorted_tab_groups_;
 
   // Owned by the Everything button.
   raw_ptr<views::MenuButtonController> menu_button_controller_;
 
+  // Whether or not the submenu of a tab group item should have "Pin/Unpin group
+  // from bookmarks bar" option. True by default. False if the submenu if
+  // visually far away from the bookmark bar e.g. in 3-dot menu.
+  bool show_pin_unpin_option_ = true;
+
   // The convenient controller that runs a context menu for saved tab group menu
   // items.
   std::unique_ptr<views::DialogModelContextMenuController>
       context_menu_controller_;
 
+  // TODO(pengchaocai): Explore a generic command id solution if there are more
+  // use cases than app menu.
+  int next_tab_group_item_command_id_ = AppMenuModel::kMinTabGroupsCommandId;
+
   std::unique_ptr<views::MenuRunner> menu_runner_;
   std::unique_ptr<ui::SimpleMenuModel> model_;
   raw_ptr<Browser> browser_;
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
index d4f76f5..98c295bc 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_interactive_uitest.cc
@@ -362,7 +362,40 @@
       WaitForShow(SavedTabGroupUtils::kMoveGroupToNewWindowMenuItem));
 }
 
-// TODO(crbug.com/40934084): Deflake this test before enabling
+// TODO(crbug.com/333956456): Resolve before enabling.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#define MAYBE_ContextMenuShowForAppMenuSubmenu \
+  DISABLED_ContextMenuShowForAppMenuSubmenu
+#else
+#define MAYBE_ContextMenuShowForAppMenuSubmenu ContextMenuShowForAppMenuSubmenu
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+IN_PROC_BROWSER_TEST_P(SavedTabGroupInteractiveTest,
+                       MAYBE_ContextMenuShowForAppMenuSubmenu) {
+  if (!IsV2UIEnabled()) {
+    GTEST_SKIP() << "N/A for V1";
+  }
+
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+
+  const tab_groups::TabGroupId local_group_id =
+      browser()->tab_strip_model()->AddToNewGroup({0});
+
+  RunTestSequence(
+      FinishTabstripAnimations(), ShowBookmarksBar(),
+      SaveGroupAndCloseEditorBubble(local_group_id),
+      WaitForHide(kTabGroupEditorBubbleId),
+      PressButton(kToolbarAppMenuButtonElementId),
+      WaitForShow(AppMenuModel::kTabGroupsMenuItem),
+      SelectMenuItem(AppMenuModel::kTabGroupsMenuItem),
+      WaitForShow(STGEverythingMenu::kTabGroup),
+      MoveMouseTo(STGEverythingMenu::kTabGroup), ClickMouse(ui_controls::RIGHT),
+      WaitForShow(SavedTabGroupUtils::kDeleteGroupMenuItem),
+      WaitForShow(SavedTabGroupUtils::kMoveGroupToNewWindowMenuItem));
+}
+
+// TODO(crbug.com/1487362): Deflake this test before enabling
 #if BUILDFLAG(IS_MAC)
 #define MAYBE_MoveGroupToNewWindowFromButtonMenu \
   DISABLED_MoveGroupToNewWindowFromButtonMenu
@@ -638,6 +671,90 @@
 }
 
 IN_PROC_BROWSER_TEST_P(SavedTabGroupInteractiveTest,
+                       CreateNewTabGroupFromAppMenuSubmenu) {
+  if (!IsV2UIEnabled()) {
+    GTEST_SKIP() << "N/A for V1";
+  }
+  const bool is_v2_ui_enabled = IsV2UIEnabled();
+
+  RunTestSequence(
+      FinishTabstripAnimations(), ShowBookmarksBar(),
+      CheckEverythingButtonVisibility(is_v2_ui_enabled),
+      CheckResult([&]() { return browser()->tab_strip_model()->count(); }, 1),
+      EnsureNotPresent(kTabGroupEditorBubbleId),
+      PressButton(kToolbarAppMenuButtonElementId),
+      WaitForShow(AppMenuModel::kTabGroupsMenuItem),
+      SelectMenuItem(AppMenuModel::kTabGroupsMenuItem),
+      WaitForShow(STGEverythingMenu::kCreateNewTabGroup),
+      SelectMenuItem(STGEverythingMenu::kCreateNewTabGroup),
+      FinishTabstripAnimations(), FlushEvents(),
+      WaitForShow(kTabGroupEditorBubbleId),
+      CheckResult([&]() { return browser()->tab_strip_model()->count(); }, 2),
+      // This menu item opens a new tab and the editor bubble.
+      CheckResult(
+          [&]() { return browser()->tab_strip_model()->active_index(); }, 1),
+      CheckResult(
+          [&]() {
+            return browser()
+                ->tab_strip_model()
+                ->GetActiveWebContents()
+                ->GetVisibleURL()
+                .host_piece();
+          },
+          chrome::kChromeUINewTabHost));
+}
+
+IN_PROC_BROWSER_TEST_P(SavedTabGroupInteractiveTest,
+                       OpenSavedGroupFromAppMenuSubmenu) {
+  if (!IsV2UIEnabled()) {
+    GTEST_SKIP() << "N/A for V1";
+  }
+
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_TRUE(
+      AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
+  ASSERT_EQ(3, browser()->tab_strip_model()->count());
+
+  // Add 2 tabs to the group.
+  const tab_groups::TabGroupId local_group_id =
+      browser()->tab_strip_model()->AddToNewGroup({0, 1});
+  const SavedTabGroupKeyedService* const service =
+      SavedTabGroupServiceFactory::GetForProfile(browser()->profile());
+
+  base::Uuid saved_guid;
+
+  RunTestSequence(
+      FinishTabstripAnimations(), ShowBookmarksBar(),
+      // Save the group and ensure it is linked in the model.
+      SaveGroupLeaveEditorBubbleOpen(local_group_id),
+      // The group we just saved should be the only group in the model.
+      CheckResult([&]() { return service->model()->Count(); }, 1),
+      // Find the saved guid that is linked to the group we just saved.
+      Do([&]() {
+        const SavedTabGroup& saved_group =
+            service->model()->saved_tab_groups()[0];
+        ASSERT_TRUE(saved_group.local_group_id().has_value());
+        saved_guid = saved_group.saved_guid();
+      }),
+      // Make sure the editor bubble is still open and flush events before we
+      // close it.
+      EnsurePresent(kTabGroupEditorBubbleId), FlushEvents(),
+      // Close the tab group and expect the saved group is no longer linked.
+      PressButton(kTabGroupEditorBubbleCloseGroupButtonId),
+      FinishTabstripAnimations(), CheckIfSavedGroupIsClosed(&saved_guid),
+      WaitForHide(kTabGroupHeaderElementId),
+      // Open the saved tab group from the submenu of "Tab groups" item in app
+      // menu.
+      PressButton(kToolbarAppMenuButtonElementId),
+      WaitForShow(AppMenuModel::kTabGroupsMenuItem),
+      SelectMenuItem(AppMenuModel::kTabGroupsMenuItem),
+      WaitForShow(STGEverythingMenu::kTabGroup),
+      SelectMenuItem(STGEverythingMenu::kTabGroup), FinishTabstripAnimations(),
+      WaitForShow(kTabGroupHeaderElementId));
+}
+
+IN_PROC_BROWSER_TEST_P(SavedTabGroupInteractiveTest,
                        EverythingButtonAlwaysShowsForV2) {
   const tab_groups::TabGroupId group_id =
       browser()->tab_strip_model()->AddToNewGroup({0});
diff --git a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
index 165eb11..e0782b3 100644
--- a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "base/functional/callback_helpers.h"
 #include "base/run_loop.h"
+#include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -20,6 +21,7 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -211,6 +213,13 @@
       ->extension_service()
       ->AddExtension(extension.get());
 
+  std::unique_ptr<web_app::OsIntegrationTestOverrideBlockingRegistration>
+      faked_os_integration;
+  {
+    base::ScopedAllowBlockingForTesting blocking;
+    faked_os_integration = std::make_unique<
+        web_app::OsIntegrationTestOverrideBlockingRegistration>();
+  }
   const GURL start_url = GURL("https://test.com/");
   auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>();
   web_app_info->start_url = start_url;
@@ -239,6 +248,11 @@
                              extensions::UNINSTALL_SOURCE_FOR_TESTING);
     run_loop.RunUntilIdle();
   }
+
+  {
+    base::ScopedAllowBlockingForTesting blocking;
+    faked_os_integration.reset();
+  }
 }
 
 class ParameterizedExtensionUninstallDialogViewBrowserTest
diff --git a/chrome/browser/ui/views/frame/browser_frame_browsertest.cc b/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
index 6cf6cfd..180bc95 100644
--- a/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/views/chrome_views_delegate.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/common/pref_names.h"
@@ -71,6 +72,9 @@
  public:
   BrowserFrameTest()
       : InProcessBrowserTest(std::make_unique<BrowserFrameBoundsChecker>()) {}
+
+ private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 // Verifies that the tools are loaded with initial bounds.
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_browsertest_win.cc b/chrome/browser/ui/views/frame/browser_frame_view_browsertest_win.cc
index 99cdc8a..3e21809f 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_browsertest_win.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_browsertest_win.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/frame/browser_frame_view_win.h"
-
 #include <tuple>
 
 #include "base/files/file_util.h"
@@ -17,6 +15,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
 #include "chrome/browser/ui/views/frame/browser_caption_button_container_win.h"
+#include "chrome/browser/ui/views/frame/browser_frame_view_win.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/windows_caption_button.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_test_helper.h"
@@ -25,6 +24,7 @@
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/win/titlebar_config.h"
@@ -188,6 +188,9 @@
       nullptr;
   raw_ptr<WebAppFrameToolbarView, AcrossTasksDanglingUntriaged>
       web_app_frame_toolbar_ = nullptr;
+
+ private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 IN_PROC_BROWSER_TEST_F(WebAppBrowserFrameViewWinTest, ThemeColor) {
@@ -336,6 +339,7 @@
   WebAppFrameToolbarTestHelper web_app_frame_toolbar_helper_;
 
  private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::ScopedTempDir temp_dir_;
 };
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
index dfcaebe6..a97f12c 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/views/location_bar/custom_tab_bar_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_utils.h"
@@ -124,6 +125,8 @@
 
  private:
   GURL GetAppURL() { return embedded_test_server()->GetURL("/empty.html"); }
+
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 // Tests the frame color for a normal browser window.
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
index ffe4f16..d245286 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -139,6 +140,7 @@
               theme_mode == ThemeMode::kDefault);
   }
 
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   raw_ptr<BrowserView, AcrossTasksDanglingUntriaged> browser_view_ = nullptr;
   raw_ptr<OpaqueBrowserFrameView, AcrossTasksDanglingUntriaged>
       opaque_browser_frame_view_ = nullptr;
@@ -338,6 +340,7 @@
   WebAppFrameToolbarTestHelper web_app_frame_toolbar_helper_;
 
  private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::ScopedTempDir temp_dir_;
 };
 
diff --git a/chrome/browser/ui/views/infobars/default_browser_infobar_interactive_uitest.cc b/chrome/browser/ui/views/infobars/default_browser_infobar_interactive_uitest.cc
index 4fc336c..0228345 100644
--- a/chrome/browser/ui/views/infobars/default_browser_infobar_interactive_uitest.cc
+++ b/chrome/browser/ui/views/infobars/default_browser_infobar_interactive_uitest.cc
@@ -142,8 +142,15 @@
       InSameContext(EnsureNotPresent(ConfirmInfoBar::kInfoBarElementId)));
 }
 
+// TODO(crbug.com/335474941): Flaky on Windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_RemovesAllBrowserPromptsOnAccept \
+  DISABLED_RemovesAllBrowserPromptsOnAccept
+#else
+#define MAYBE_RemovesAllBrowserPromptsOnAccept RemovesAllBrowserPromptsOnAccept
+#endif
 IN_PROC_BROWSER_TEST_F(DefaultBrowserInfobarWithRefreshInteractiveTest,
-                       RemovesAllBrowserPromptsOnAccept) {
+                       MAYBE_RemovesAllBrowserPromptsOnAccept) {
   DefaultBrowserPromptManager::GetInstance()->MaybeShowPrompt();
   RunTestSequence(
       WaitForShow(ConfirmInfoBar::kInfoBarElementId), FlushEvents(),
@@ -172,8 +179,16 @@
 }
 #endif
 
+// TODO(crbug.com/335474941): Flaky on Windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_HandlesAcceptWithDisabledAnimation \
+  DISABLED_HandlesAcceptWithDisabledAnimation
+#else
+#define MAYBE_HandlesAcceptWithDisabledAnimation \
+  HandlesAcceptWithDisabledAnimation
+#endif
 IN_PROC_BROWSER_TEST_F(DefaultBrowserInfobarWithRefreshInteractiveTest,
-                       HandlesAcceptWithDisabledAnimation) {
+                       MAYBE_HandlesAcceptWithDisabledAnimation) {
   // When animations are disabled, the info bar is destroyed sooner which can
   // cause UAF if not handled properly. This test ensures it is handled
   // properly.
@@ -200,8 +215,14 @@
                   WaitForHide(ConfirmInfoBar::kInfoBarElementId));
 }
 
+// TODO(crbug.com/335474941): Flaky on Windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_LogsMetrics DISABLED_LogsMetrics
+#else
+#define MAYBE_LogsMetrics LogsMetrics
+#endif
 IN_PROC_BROWSER_TEST_F(DefaultBrowserInfobarWithRefreshInteractiveTest,
-                       LogsMetrics) {
+                       MAYBE_LogsMetrics) {
   base::HistogramTester histogram_tester;
   DefaultBrowserPromptManager::GetInstance()->MaybeShowPrompt();
   RunTestSequence(WaitForShow(ConfirmInfoBar::kInfoBarElementId),
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc
index cd4caf0..fea2b16 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc
@@ -951,7 +951,7 @@
   webapps::AppId app_id_;
 
   // Stop test from installing OS hooks.
-  web_app::OsIntegrationManager::ScopedSuppressForTesting os_hooks_suppress_;
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
 };
 
 // Test renamed, as currently Skia Gold doesn't support resetting test
diff --git a/chrome/browser/ui/views/site_data/page_specific_site_data_dialog_interactive_uitest.cc b/chrome/browser/ui/views/site_data/page_specific_site_data_dialog_interactive_uitest.cc
index fc05c48..ea43fc7 100644
--- a/chrome/browser/ui/views/site_data/page_specific_site_data_dialog_interactive_uitest.cc
+++ b/chrome/browser/ui/views/site_data/page_specific_site_data_dialog_interactive_uitest.cc
@@ -402,26 +402,6 @@
   ~PageSpecificSiteDataDialogIsolatedWebAppInteractiveUiTest() override =
       default;
 
-  void SetUpOnMainThread() override {
-#if !BUILDFLAG(IS_MAC)
-    // TODO(crbug.com/40272260): OsIntegrationTestOverrideImpl seems
-    // to interfere with Kombucha on the Mac.
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    override_registration_ =
-        web_app::OsIntegrationTestOverrideImpl::OverrideForTesting();
-#endif  // BUILDFLAG(IS_MAC)
-    PageSpecificSiteDataDialogInteractiveUiTest::SetUpOnMainThread();
-  }
-  void TearDownOnMainThread() override {
-    web_app::test::UninstallWebApp(browser()->profile(), app_id_);
-
-#if !BUILDFLAG(IS_MAC)
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    override_registration_.reset();
-#endif  // BUILDFLAG(IS_MAC)
-    PageSpecificSiteDataDialogInteractiveUiTest::TearDownOnMainThread();
-  }
-
  protected:
   void SetUpFeatureList() override {
     feature_list_.InitWithFeatures(
@@ -474,16 +454,22 @@
 
  private:
   webapps::AppId app_id_;
-#if !BUILDFLAG(IS_MAC)
-  std::unique_ptr<
-      ::web_app::OsIntegrationTestOverrideImpl::BlockingRegistration>
+  web_app::OsIntegrationTestOverrideImpl::BlockingRegistration
       override_registration_;
-#endif  // !BUILDFLAG(IS_MAC)
 };
 
+// TODO(crbug.com/40776475): This test fails to pass on Mac with real app shims
+// working.
+#if BUILDFLAG(IS_MAC)
+#define MAYBE_AppNameIsDisplayedInsteadOfHostname \
+  DISABLED_AppNameIsDisplayedInsteadOfHostname
+#else
+#define MAYBE_AppNameIsDisplayedInsteadOfHostname \
+  AppNameIsDisplayedInsteadOfHostname
+#endif  // BUILDFLAG(IS_MAC)
 IN_PROC_BROWSER_TEST_F(
     PageSpecificSiteDataDialogIsolatedWebAppInteractiveUiTest,
-    AppNameIsDisplayedInsteadOfHostname) {
+    MAYBE_AppNameIsDisplayedInsteadOfHostname) {
   Browser* iwa_browser = InstallAndLaunchIsolatedWebApp();
   RunTestSequenceInContext(
       iwa_browser->window()->GetElementContext(),
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
index 19bc0461..8dcc8f73 100644
--- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
@@ -61,10 +61,6 @@
   return nullptr;
 }
 
-bool FakeTabSlotController::ShowDomainInHoverCards() const {
-  return true;
-}
-
 bool FakeTabSlotController::HoverCardIsShowingForTab(Tab* tab) {
   return false;
 }
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
index a309ac7..980f955 100644
--- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
@@ -68,7 +68,6 @@
   void OnMouseEventInTab(views::View* source,
                          const ui::MouseEvent& event) override {}
   void UpdateHoverCard(Tab* tab, HoverCardUpdateType update_type) override {}
-  bool ShowDomainInHoverCards() const override;
   bool HoverCardIsShowingForTab(Tab* tab) override;
   int GetBackgroundOffset() const override;
   int GetStrokeThickness() const override;
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 0bd51294..146c8621 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -49,6 +49,7 @@
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/tabs/window_finder.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
@@ -3182,6 +3183,7 @@
   }
 
  private:
+  web_app::OsIntegrationTestOverrideBlockingRegistration faked_os_integration_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index b3b8bba..2531327a 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -28,11 +28,8 @@
 #include "chrome/browser/ui/views/tabs/fade_label_view.h"
 #include "chrome/browser/ui/views/tabs/filename_elider.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
-#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_style_views.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/prefs/pref_service.h"
 #include "components/url_formatter/url_formatter.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -138,6 +135,10 @@
     SetLayoutManager(std::make_unique<views::FillLayout>());
   }
 
+  void SetAnimationEnabled(bool animation_enabled) {
+    animation_enabled_ = animation_enabled;
+  }
+
   // Sets the appropriate rounded corners for the preview image, for platforms
   // where layers must be explicitly clipped (because they are not clipped by
   // the widget).
@@ -283,7 +284,9 @@
       return;
     }
 
-    if (!GetPreviewImageCrossfadeStart().has_value()) {
+    // For consistency, always bail out with a "don't crossfade" response if
+    // animations are disabled.
+    if (!animation_enabled_ || !GetPreviewImageCrossfadeStart().has_value()) {
       return;
     }
 
@@ -320,6 +323,8 @@
 
   const raw_ptr<TabHoverCardBubbleView> bubble_view_;
 
+  bool animation_enabled_ = true;
+
   // Displays the image that we are trying to display for the target/current
   // tab. Placed under `image_fading_out_` so that it is revealed as the
   // previous image fades out.
@@ -351,12 +356,16 @@
 
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(TabHoverCardBubbleView,
                                       kHoverCardBubbleElementId);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(TabHoverCardBubbleView,
+                                      kHoverCardDomainLabelElementId);
 
-TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab)
+TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab,
+                                               const InitParams& params)
     : BubbleDialogDelegateView(tab,
                                views::BubbleBorder::TOP_LEFT,
                                views::BubbleBorder::STANDARD_SHADOW),
-      tab_style_(TabStyle::Get()) {
+      tab_style_(TabStyle::Get()),
+      bubble_params_(params) {
   SetButtons(ui::DIALOG_BUTTON_NONE);
 
   // Remove the accessible role so that hover cards are not read when they
@@ -393,8 +402,9 @@
         1, views::style::CONTEXT_DIALOG_BODY_TEXT));
   }
 
-  if (TabHoverCardController::AreHoverCardImagesEnabled()) {
+  if (bubble_params_.show_image_preview) {
     thumbnail_view_ = AddChildView(std::make_unique<ThumbnailView>(this));
+    thumbnail_view_->SetAnimationEnabled(bubble_params_.use_animation);
     thumbnail_view_->SetRoundedCorners(true, corner_radius_);
   }
 
@@ -405,14 +415,6 @@
           footer_view_->flex_layout()->GetDefaultFlexRule())
           .WithWeight(0));
 
-  OnMemoryUsageInHovercardsPrefChanged();
-  pref_change_registrar_.Init(g_browser_process->local_state());
-  pref_change_registrar_.Add(
-      prefs::kHoverCardMemoryUsageEnabled,
-      base::BindRepeating(
-          &TabHoverCardBubbleView::OnMemoryUsageInHovercardsPrefChanged,
-          base::Unretained(this)));
-
   // Set up layout.
 
   views::FlexLayout* const layout =
@@ -426,12 +428,13 @@
   // label. In those cases, we need to adjust the bottom margin of the title
   // element because it is no longer above another text element and needs a
   // bottom margin.
-  const bool show_domain = tab->controller()->ShowDomainInHoverCards();
 
   gfx::Insets title_margins =
       features::IsChromeRefresh2023() ? kTextAreaRefreshMargins : kTitleMargins;
-  domain_label_->SetVisible(show_domain);
-  if (show_domain) {
+  domain_label_->SetVisible(bubble_params_.show_domain);
+  domain_label_->SetProperty(views::kElementIdentifierKey,
+                             kHoverCardDomainLabelElementId);
+  if (bubble_params_.show_domain) {
     gfx::Insets domain_margins = title_margins;
     domain_margins.set_top(features::IsChromeRefresh2023() ? kTitleDomainSpacing
                                                            : 0);
@@ -561,7 +564,7 @@
   // High memory usage notification is considered a tab alert. Show it even
   // if the memory usage in hovercards pref is disabled.
   const bool show_memory_usage =
-      (memory_usage_in_hovercards_setting_ && tab_memory_usage_in_bytes > 0) ||
+      (bubble_params_.show_memory_usage && tab_memory_usage_in_bytes > 0) ||
       is_high_memory_usage;
   bool show_footer =
       alert_state_.has_value() || show_discard_status || show_memory_usage;
@@ -616,12 +619,6 @@
 
 // static
 std::optional<double> TabHoverCardBubbleView::GetPreviewImageCrossfadeStart() {
-  // For consistency, always bail out with a "don't crossfade" response if
-  // animations are disabled.
-  if (!TabHoverCardController::UseAnimations()) {
-    return std::nullopt;
-  }
-
   static const double start_percent = base::GetFieldTrialParamByFeatureAsDouble(
       features::kTabHoverCardImages,
       features::kTabHoverCardImagesCrossfadePreviewAtParameterName, 0.25);
@@ -630,12 +627,6 @@
              : std::nullopt;
 }
 
-void TabHoverCardBubbleView::OnMemoryUsageInHovercardsPrefChanged() {
-  PrefService* const pref_service = g_browser_process->local_state();
-  memory_usage_in_hovercards_setting_ =
-      pref_service->GetBoolean(prefs::kHoverCardMemoryUsageEnabled);
-}
-
 gfx::Size TabHoverCardBubbleView::CalculatePreferredSize() const {
   const int width = tab_style_->GetPreviewImageSize().width();
   const int height =
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
index c50e5742..5d8a6b32 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h
@@ -19,7 +19,6 @@
 #include "chrome/browser/ui/tabs/tab_utils.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/tabs/fade_footer_view.h"
-#include "components/prefs/pref_change_registrar.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/views/animation/animation_delegate_views.h"
@@ -46,7 +45,20 @@
       base::Milliseconds(200);
 
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHoverCardBubbleElementId);
-  explicit TabHoverCardBubbleView(Tab* tab);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHoverCardDomainLabelElementId);
+
+  struct InitParams {
+    // Becomes false with certain accessibility options and in tests
+    bool use_animation = true;
+    // Becomes false for ChromeOS tabbed system apps (e.g. Terminal)
+    bool show_domain = true;
+    // Becomes false when image preview setting is disabled
+    bool show_image_preview = true;
+    // Becomes false for ChromeOS system apps or when disabled in settings
+    bool show_memory_usage = true;
+  };
+
+  explicit TabHoverCardBubbleView(Tab* tab, const InitParams& params);
   TabHoverCardBubbleView(const TabHoverCardBubbleView&) = delete;
   TabHoverCardBubbleView& operator=(const TabHoverCardBubbleView&) = delete;
   ~TabHoverCardBubbleView() override;
@@ -80,8 +92,6 @@
                            BackgroundTabHoverCardContentsHaveCorrectDimensions);
   class ThumbnailView;
 
-  void OnMemoryUsageInHovercardsPrefChanged();
-
   // views::BubbleDialogDelegateView:
   gfx::Size CalculatePreferredSize() const override;
 
@@ -91,9 +101,8 @@
   raw_ptr<FooterView> footer_view_ = nullptr;
   std::optional<TabAlertState> alert_state_;
   const raw_ptr<const TabStyle> tab_style_;
-  PrefChangeRegistrar pref_change_registrar_;
-  bool memory_usage_in_hovercards_setting_ = false;
 
+  const InitParams bubble_params_;
   int corner_radius_ = ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
       views::Emphasis::kHigh);
 };
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller.cc b/chrome/browser/ui/views/tabs/tab_hover_card_controller.cc
index de0f55e..b0b2cdd 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/ui/views/tabs/tab_hover_card_thumbnail_observer.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
+#include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/common/pref_names.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/omnibox/browser/omnibox_popup_view.h"
@@ -145,6 +146,16 @@
   return delay;
 }
 
+bool IsBrowserForSystemWebApp(const Browser* browser) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  const auto* const app_controller = browser->app_controller();
+  if (app_controller && app_controller->system_app()) {
+    return true;
+  }
+#endif
+  return false;
+}
+
 }  // anonymous namespace
 
 //-------------------------------------------------------------------
@@ -210,14 +221,26 @@
                                       base::Value(base::FeatureList::IsEnabled(
                                           features::kTabHoverCardImages)));
 
+    pref_change_registrar_.Init(pref_service);
+
     // Register for previews enabled pref change events.
     hover_card_image_previews_enabled_ = AreHoverCardImagesEnabled();
-    pref_change_registrar_.Init(pref_service);
     pref_change_registrar_.Add(
         prefs::kHoverCardImagesEnabled,
         base::BindRepeating(
             &TabHoverCardController::OnHovercardImagesEnabledChanged,
             base::Unretained(this)));
+
+    // Register for memory usage enabled pref change events. Exclude
+    // tracking them for system web apps (e.g. ChromeOS terminal app).
+    if (!IsBrowserForSystemWebApp(tab_strip_->GetBrowser())) {
+      OnHovercardMemoryUsageEnabledChanged();
+      pref_change_registrar_.Add(
+          prefs::kHoverCardMemoryUsageEnabled,
+          base::BindRepeating(
+              &TabHoverCardController::OnHovercardMemoryUsageEnabledChanged,
+              base::Unretained(this)));
+    }
   }
 
   // Possibly apply memory pressure override for testing.
@@ -500,7 +523,14 @@
 }
 
 void TabHoverCardController::CreateHoverCard(Tab* tab) {
-  hover_card_ = new TabHoverCardBubbleView(tab);
+  TabHoverCardBubbleView::InitParams params;
+  params.use_animation = UseAnimations();
+  // In some browser types (e.g. ChromeOS terminal app) hide the domain label.
+  params.show_domain = !IsBrowserForSystemWebApp(tab_strip_->GetBrowser());
+  params.show_memory_usage = hover_card_memory_usage_enabled_;
+  params.show_image_preview = hover_card_image_previews_enabled_;
+
+  hover_card_ = new TabHoverCardBubbleView(tab, params);
   hover_card_observation_.Observe(hover_card_.get());
   event_sniffer_ = std::make_unique<EventSniffer>(this);
   slide_animator_ = std::make_unique<views::BubbleSlideAnimator>(hover_card_);
@@ -574,7 +604,8 @@
   // The crossfade parameter determines when a placeholder image is displayed.
   const auto crossfade_at =
       TabHoverCardBubbleView::GetPreviewImageCrossfadeStart();
-  if (crossfade_at.has_value() && crossfade_at.value() == 0.0) {
+  if (UseAnimations() && crossfade_at.has_value() &&
+      crossfade_at.value() == 0.0) {
     hover_card_->SetPlaceholderImage();
     thumbnail_wait_state_ = ThumbnailWaitState::kWaitingWithPlaceholder;
   } else {
@@ -806,3 +837,9 @@
     thumbnail_observer_.reset();
   }
 }
+
+void TabHoverCardController::OnHovercardMemoryUsageEnabledChanged() {
+  hover_card_memory_usage_enabled_ =
+      g_browser_process->local_state()->GetBoolean(
+          prefs::kHoverCardMemoryUsageEnabled);
+}
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller.h b/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
index 7292f3e..88ab91d 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
@@ -40,13 +40,6 @@
   explicit TabHoverCardController(TabStrip* tab_strip);
   ~TabHoverCardController() override;
 
-  // Returns whether the hover card preview images feature is enabled.
-  static bool AreHoverCardImagesEnabled();
-
-  // Returns whether hover card animations should be shown on the current
-  // device.
-  static bool UseAnimations();
-
   bool IsHoverCardVisible() const;
   bool IsHoverCardShowingForTab(Tab* tab) const;
   void UpdateHoverCard(Tab* tab,
@@ -77,7 +70,10 @@
   FRIEND_TEST_ALL_PREFIXES(TabHoverCardControllerTest,
                            HidePreviewsForDiscardedTab);
   FRIEND_TEST_ALL_PREFIXES(TabHoverCardControllerTest,
+                           DisableMemoryUsageForTab);
+  FRIEND_TEST_ALL_PREFIXES(TabHoverCardControllerTest,
                            ShowPreviewsForDiscardedTabWithThumbnail);
+  FRIEND_TEST_ALL_PREFIXES(TabHoverCardPreviewsEnabledPrefTest, DefaultState);
   class EventSniffer;
 
   enum ThumbnailWaitState {
@@ -86,6 +82,13 @@
     kWaitingWithoutPlaceholder
   };
 
+  // Returns whether the hover card preview images feature is enabled.
+  static bool AreHoverCardImagesEnabled();
+
+  // Returns whether hover card animations should be shown on the current
+  // device.
+  static bool UseAnimations();
+
   // views::ViewObserver:
   void OnViewIsDeleting(views::View* observed_view) override;
   void OnViewVisibilityChanged(views::View* observed_view,
@@ -134,6 +137,7 @@
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
 
   void OnHovercardImagesEnabledChanged();
+  void OnHovercardMemoryUsageEnabledChanged();
 
   bool waiting_for_preview() const {
     return thumbnail_wait_state_ != ThumbnailWaitState::kNotWaiting;
@@ -184,9 +188,10 @@
   // resources are up to date when we eventually show the hover card.
   raw_ptr<TabResourceUsageCollector> tab_resource_usage_collector_;
 
-  // Tracks changes to the hover card image previews preferences
+  // Tracks changes to the hover card preferences
   PrefChangeRegistrar pref_change_registrar_;
   bool hover_card_image_previews_enabled_ = false;
+  bool hover_card_memory_usage_enabled_ = false;
 
   // Ensure that this timer is destroyed before anything else is cleaned up.
   base::OneShotTimer delayed_show_timer_;
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc
index 28c224f..a055389 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
-
 #include <memory>
 
 #include "base/strings/utf_string_conversions.h"
@@ -28,8 +26,12 @@
 #include "chrome/browser/ui/views/tabs/tab.h"
 #include "chrome/browser/ui/views/tabs/tab_close_button.h"
 #include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_hover_card_test_util.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
@@ -59,6 +61,10 @@
 #include "ui/views/test/widget_test.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h"
+#endif
+
 namespace {
 constexpr char16_t kTabTitle[] = u"Test Tab 2";
 constexpr char16_t kTabDomain[] = u"example.com";
@@ -799,3 +805,40 @@
     All,
     TabHoverCardFadeFooterInteractiveUiTest,
     testing::ValuesIn(GetTabHoverCardFooterTestFeatureConfig()));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+class TabHoverCardSystemWebAppTest : public InteractiveBrowserTest {
+ public:
+  TabHoverCardSystemWebAppTest()
+      : test_system_web_app_installation_(
+            ash::TestSystemWebAppInstallation::SetUpTabbedMultiWindowApp()) {}
+
+  void SetUpOnMainThread() override {
+    InteractiveBrowserTest::SetUpOnMainThread();
+    Tab::SetShowHoverCardOnMouseHoverForTesting(true);
+  }
+
+ protected:
+  std::unique_ptr<ash::TestSystemWebAppInstallation>
+      test_system_web_app_installation_;
+};
+
+IN_PROC_BROWSER_TEST_F(TabHoverCardSystemWebAppTest,
+                       HideDomainNameFromHoverCard) {
+  test_system_web_app_installation_->WaitForAppInstall();
+  const auto* const app_browser = web_app::LaunchWebAppBrowser(
+      browser()->profile(), test_system_web_app_installation_->GetAppId());
+  const char kTabToHover[] = "Tab to hover";
+
+  RunTestSequenceInContext(
+      app_browser->window()->GetElementContext(),
+      WithView(kTabStripElementId,
+               [](TabStrip* tab_strip) { tab_strip->StopAnimating(true); }),
+      NameDescendantViewByType<Tab>(kBrowserViewElementId, kTabToHover, 0),
+      MoveMouseTo(kTabToHover),
+      WaitForShow(TabHoverCardBubbleView::kHoverCardBubbleElementId),
+      EnsureNotPresent(TabHoverCardBubbleView::kHoverCardDomainLabelElementId),
+      MoveMouseTo(kNewTabButtonElementId),
+      WaitForHide(TabHoverCardBubbleView::kHoverCardBubbleElementId));
+}
+#endif
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller_unittest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_controller_unittest.cc
index 964ce02..81ef0537 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_controller_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller_unittest.cc
@@ -110,6 +110,29 @@
             TabHoverCardController::kNotWaiting);
 }
 
+TEST_F(TabHoverCardControllerTest, DisableMemoryUsageForTab) {
+  g_browser_process->local_state()->SetBoolean(
+      prefs::kHoverCardMemoryUsageEnabled, false);
+
+  AddTab(browser_view()->browser(), GURL("http://foo1.com"));
+  AddTab(browser_view()->browser(), GURL("http://foo2.com"));
+  browser_view()->browser()->tab_strip_model()->ActivateTabAt(0);
+
+  auto controller =
+      std::make_unique<TabHoverCardController>(browser_view()->tabstrip());
+
+  Tab* const target_tab = browser_view()->tabstrip()->tab_at(1);
+  TabRendererData data;
+  auto tab_resource_usage = base::MakeRefCounted<TabResourceUsage>();
+  tab_resource_usage->SetMemoryUsageInBytes(100);
+  data.tab_resource_usage = std::move(tab_resource_usage);
+  target_tab->SetData(std::move(data));
+  controller->target_tab_ = target_tab;
+
+  controller->CreateHoverCard(target_tab);
+  EXPECT_FALSE(controller->hover_card_memory_usage_enabled_);
+}
+
 class TestThumbnailImageDelegate : public ThumbnailImage::Delegate {
  public:
   TestThumbnailImageDelegate() = default;
diff --git a/chrome/browser/ui/views/tabs/tab_slot_controller.h b/chrome/browser/ui/views/tabs/tab_slot_controller.h
index d1c5c89..e928216 100644
--- a/chrome/browser/ui/views/tabs/tab_slot_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_slot_controller.h
@@ -156,9 +156,6 @@
   // how the show, hide, or update will be processed.
   virtual void UpdateHoverCard(Tab* tab, HoverCardUpdateType update_type) = 0;
 
-  // Returns whether domain/origin should be shown in tab hover cards.
-  virtual bool ShowDomainInHoverCards() const = 0;
-
   // Returns true if the hover card is showing for the given tab.
   virtual bool HoverCardIsShowingForTab(Tab* tab) = 0;
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 4308001b..fe75f65 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1767,16 +1767,6 @@
   tab_container_->UpdateHoverCard(tab, update_type);
 }
 
-bool TabStrip::ShowDomainInHoverCards() const {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  const auto* app_controller = GetBrowser()->app_controller();
-  if (app_controller && app_controller->system_app()) {
-    return false;
-  }
-#endif
-  return true;
-}
-
 bool TabStrip::HoverCardIsShowingForTab(Tab* tab) {
   return hover_card_controller_ &&
          hover_card_controller_->IsHoverCardShowingForTab(tab);
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 4551481..d3919c76 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -288,7 +288,6 @@
   void OnMouseEventInTab(views::View* source,
                          const ui::MouseEvent& event) override;
   void UpdateHoverCard(Tab* tab, HoverCardUpdateType update_type) override;
-  bool ShowDomainInHoverCards() const override;
   bool HoverCardIsShowingForTab(Tab* tab) override;
   int GetBackgroundOffset() const override;
   int GetStrokeThickness() const override;
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index d5d4395..eee57e09 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/profiles/profile_view_utils.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_service_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
 #include "chrome/browser/ui/ui_features.h"
@@ -52,6 +53,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/bookmarks/browser/bookmark_model.h"
+#include "components/saved_tab_groups/features.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/zoom/page_zoom.h"
@@ -123,6 +125,13 @@
 // Horizontal padding on the edges of the in-menu buttons.
 const int kHorizontalPadding = 15;
 
+constexpr int kBookmarksCommandIdOffset =
+    AppMenuModel::kMinBookmarksCommandId - IDC_FIRST_UNBOUNDED_MENU;
+constexpr int kRecentTabsCommandIdOffset =
+    AppMenuModel::kMinRecentTabsCommandId - IDC_FIRST_UNBOUNDED_MENU;
+constexpr int kTabGroupsCommandIdOffset =
+    AppMenuModel::kMinTabGroupsCommandId - IDC_FIRST_UNBOUNDED_MENU;
+
 #if BUILDFLAG(IS_CHROMEOS)
 // Extra horizontal space to reserve for the fullscreen button.
 const int kFullscreenPadding = 74;
@@ -138,7 +147,7 @@
   return command_id >= IDC_FIRST_UNBOUNDED_MENU &&
          ((command_id - IDC_FIRST_UNBOUNDED_MENU) %
               AppMenuModel::kNumUnboundedMenuTypes ==
-          0);
+          kBookmarksCommandIdOffset);
 }
 
 // Returns true if |command_id| identifies a recent tabs menu item.
@@ -146,7 +155,15 @@
   return command_id >= IDC_FIRST_UNBOUNDED_MENU &&
          ((command_id - IDC_FIRST_UNBOUNDED_MENU) %
               AppMenuModel::kNumUnboundedMenuTypes ==
-          1);
+          kRecentTabsCommandIdOffset);
+}
+
+// Returns true if |command_id| identifies a tab group menu item.
+bool IsTabGroupsCommand(int command_id) {
+  return command_id >= IDC_FIRST_UNBOUNDED_MENU &&
+         ((command_id - IDC_FIRST_UNBOUNDED_MENU) %
+              AppMenuModel::kNumUnboundedMenuTypes ==
+          kTabGroupsCommandIdOffset);
 }
 
 // Combination border/background for the buttons contained in the menu. The
@@ -1106,6 +1123,11 @@
                               int command_id,
                               const gfx::Point& p,
                               ui::MenuSourceType source_type) {
+  if (IsTabGroupsCommand(command_id)) {
+    stg_everything_menu_->SetShowPinOption(false);
+    return stg_everything_menu_->ShowContextMenu(source, command_id, p,
+                                                 source_type);
+  }
   return IsBookmarkCommand(command_id)
              ? bookmark_menu_delegate_->ShowContextMenu(source, command_id, p,
                                                         source_type)
@@ -1150,6 +1172,11 @@
     return false;  // The root item, a separator, or a title.
   }
 
+  if (command_id == IDC_CREATE_NEW_TAB_GROUP ||
+      IsTabGroupsCommand(command_id)) {
+    return true;
+  }
+
   if (IsBookmarkCommand(command_id) ||
       command_id == IDC_SHOW_BOOKMARK_SIDE_PANEL) {
     return true;
@@ -1183,6 +1210,12 @@
 }
 
 void AppMenu::ExecuteCommand(int command_id, int mouse_event_flags) {
+  if (command_id == IDC_CREATE_NEW_TAB_GROUP ||
+      IsTabGroupsCommand(command_id)) {
+    stg_everything_menu_->ExecuteCommand(command_id, mouse_event_flags);
+    return;
+  }
+
   if (IsBookmarkCommand(command_id)) {
     UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.OpenBookmark",
                                menu_opened_timer_.Elapsed());
@@ -1210,6 +1243,11 @@
     return false;
   }
 
+  if (command_id == IDC_CREATE_NEW_TAB_GROUP ||
+      IsTabGroupsCommand(command_id)) {
+    return false;
+  }
+
   if (IsBookmarkCommand(command_id) ||
       command_id == IDC_SHOW_BOOKMARK_SIDE_PANEL) {
     return false;
@@ -1232,10 +1270,17 @@
 }
 
 void AppMenu::WillShowMenu(MenuItemView* menu) {
-  if (menu == bookmark_menu_)
+  if (menu == saved_tab_groups_menu_) {
+    if (!stg_everything_menu_) {
+      stg_everything_menu_ =
+          std::make_unique<tab_groups::STGEverythingMenu>(nullptr, browser_);
+    }
+    stg_everything_menu_->PopulateMenu(menu);
+  } else if (menu == bookmark_menu_) {
     CreateBookmarkMenu();
-  else if (bookmark_menu_delegate_)
+  } else if (bookmark_menu_delegate_) {
     bookmark_menu_delegate_->WillShowMenu(menu);
+  }
 }
 
 void AppMenu::WillHideMenu(MenuItemView* menu) {
@@ -1390,6 +1435,11 @@
         bookmark_menu_ = item;
         break;
 
+      case IDC_SAVED_TAB_GROUPS_MENU:
+        DCHECK(!saved_tab_groups_menu_);
+        saved_tab_groups_menu_ = item;
+        break;
+
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
       case IDC_FEEDBACK:
         DCHECK(!feedback_menu_item_);
diff --git a/chrome/browser/ui/views/toolbar/app_menu.h b/chrome/browser/ui/views/toolbar/app_menu.h
index c5dd0878..0d8614f 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.h
+++ b/chrome/browser/ui/views/toolbar/app_menu.h
@@ -15,7 +15,9 @@
 #include "base/timer/elapsed_timer.h"
 #include "chrome/browser/ui/global_error/global_error_observer.h"
 #include "chrome/browser/ui/global_error/global_error_service.h"
+#include "chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.h"
 #include "components/bookmarks/browser/base_bookmark_model_observer.h"
+#include "components/saved_tab_groups/saved_tab_group.h"
 #include "ui/base/models/menu_model.h"
 #include "ui/views/controls/menu/menu_delegate.h"
 
@@ -171,6 +173,12 @@
   // Menu corresponding to IDC_BOOKMARKS_MENU.
   raw_ptr<views::MenuItemView, DanglingUntriaged> bookmark_menu_ = nullptr;
 
+  // Used for managing the tab group menu items.
+  std::unique_ptr<tab_groups::STGEverythingMenu> stg_everything_menu_;
+
+  // Menu corresponding to IDC_SAVED_TAB_GROUPS_MENU.
+  raw_ptr<views::MenuItemView> saved_tab_groups_menu_ = nullptr;
+
   // Menu corresponding to IDC_FEEDBACK.
   raw_ptr<views::MenuItemView, DanglingUntriaged> feedback_menu_item_ = nullptr;
 
diff --git a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller_unittest.cc b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller_unittest.cc
index cec4b9d..35846d7 100644
--- a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller_unittest.cc
+++ b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller_unittest.cc
@@ -232,6 +232,9 @@
         std::make_unique<LoopbackCrosapiAppServiceProxy>(profile_.get());
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
+    // Launching requires real os integration.
+    fake_provider()->UseRealOsIntegrationManager();
+
     test::AwaitStartWebAppProviderAndSubsystems(profile());
   }
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index cc83c1cf..d7e5d56 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -1778,10 +1778,11 @@
       << "No app installed for site: " << static_cast<int>(site);
 
   WebAppRegistrar& app_registrar = provider()->registrar_unsafe();
-#if BUILDFLAG(IS_CHROMEOS)
-  DisplayMode display_mode = app_registrar.GetAppEffectiveDisplayMode(app_id);
-  bool is_open_in_app_browser =
+  const DisplayMode display_mode =
+      app_registrar.GetAppEffectiveDisplayMode(app_id);
+  const bool is_open_in_app_browser =
       (display_mode != blink::mojom::DisplayMode::kBrowser);
+#if BUILDFLAG(IS_CHROMEOS)
   if (is_open_in_app_browser) {
     app_browser_ = LaunchWebAppBrowserAndWait(profile(), app_id);
     active_app_id_ = app_id;
@@ -1808,15 +1809,19 @@
   event_ptr->meta_key = false;
   event_ptr->shift_key = false;
 
+  BrowserAddedWaiter browser_added_waiter;
   ui_test_utils::UrlLoadObserver url_observer(
       app_registrar.GetAppLaunchUrl(app_id),
       content::NotificationService::AllSources());
   app_home_page_handler.LaunchApp(app_id, std::move(event_ptr));
   url_observer.Wait();
 
-  // The app_browser_ is needed only for apps that open in a new window, and is
-  // nullptr for apps that launch in a tab.
-  app_browser_ = GetAppBrowserForAppId(profile(), app_id);
+  // The app_browser_ is needed only for apps that open in a new window.
+  if (is_open_in_app_browser) {
+    browser_added_waiter.Wait();
+    app_browser_ = browser_added_waiter.browser_added();
+    EXPECT_TRUE(AppBrowserController::IsForWebApp(app_browser(), app_id));
+  }
   active_app_id_ = app_id;
 #endif
   AfterStateChangeAction();
diff --git a/chrome/browser/ui/views/webauthn/authenticator_gpm_arbitrary_pin_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_gpm_arbitrary_pin_sheet_view.cc
index 0470998..cdabfe1 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_gpm_arbitrary_pin_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_gpm_arbitrary_pin_sheet_view.cc
@@ -21,10 +21,11 @@
 std::pair<std::unique_ptr<views::View>,
           AuthenticatorGPMArbitraryPinSheetView::AutoFocus>
 AuthenticatorGPMArbitraryPinSheetView::BuildStepSpecificContent() {
-  return std::make_pair(std::make_unique<AuthenticatorGPMArbitraryPinView>(
-                            gpm_arbitrary_pin_sheet_model()->ui_disabled(),
-                            gpm_arbitrary_pin_sheet_model()->pin(), this),
-                        AutoFocus::kYes);
+  bool ui_disabled = gpm_arbitrary_pin_sheet_model()->ui_disabled();
+  return std::make_pair(
+      std::make_unique<AuthenticatorGPMArbitraryPinView>(
+          ui_disabled, gpm_arbitrary_pin_sheet_model()->pin(), this),
+      ui_disabled ? AutoFocus::kNo : AutoFocus::kYes);
 }
 
 void AuthenticatorGPMArbitraryPinSheetView::OnPinChanged(std::u16string pin) {
diff --git a/chrome/browser/ui/views/webauthn/authenticator_gpm_arbitrary_pin_view.cc b/chrome/browser/ui/views/webauthn/authenticator_gpm_arbitrary_pin_view.cc
index c5f9ddc..99a18e8 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_gpm_arbitrary_pin_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_gpm_arbitrary_pin_view.cc
@@ -26,6 +26,7 @@
   pin_textfield->SetDefaultWidthInChars(20);
   pin_textfield->SetReadOnly(ui_disabled);
   pin_textfield->SetText(pin);
+  pin_textfield->SetEnabled(!ui_disabled);
   pin_textfield_ = AddChildView(std::move(pin_textfield));
 
   reveal_button_ = AddChildView(CreateRevealButton(base::BindRepeating(
diff --git a/chrome/browser/ui/views/webauthn/authenticator_gpm_pin_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_gpm_pin_sheet_view.cc
index 0daa5099..27906ff 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_gpm_pin_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_gpm_pin_sheet_view.cc
@@ -19,11 +19,11 @@
 
 std::pair<std::unique_ptr<views::View>, AuthenticatorGpmPinSheetView::AutoFocus>
 AuthenticatorGpmPinSheetView::BuildStepSpecificContent() {
+  bool ui_disabled = gpm_pin_sheet_model()->ui_disabled();
   return std::make_pair(std::make_unique<AuthenticatorGPMPinView>(
                             gpm_pin_sheet_model()->pin_digits_count(),
-                            gpm_pin_sheet_model()->ui_disabled(),
-                            gpm_pin_sheet_model()->pin(), this),
-                        AutoFocus::kYes);
+                            ui_disabled, gpm_pin_sheet_model()->pin(), this),
+                        ui_disabled ? AutoFocus::kNo : AutoFocus::kYes);
 }
 
 void AuthenticatorGpmPinSheetView::OnPinChanged(std::u16string pin) {
diff --git a/chrome/browser/ui/views/webauthn/authenticator_gpm_pin_view.cc b/chrome/browser/ui/views/webauthn/authenticator_gpm_pin_view.cc
index 0f943b27..36aca8e 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_gpm_pin_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_gpm_pin_view.cc
@@ -26,6 +26,7 @@
   pin_textfield->SetObscured(true);
   pin_textfield->SetDisabled(ui_disabled);
   pin_textfield->SetPin(pin);
+  pin_textfield->SetEnabled(!ui_disabled);
   pin_textfield_ = AddChildView(std::move(pin_textfield));
 
   reveal_button_ = AddChildView(CreateRevealButton(
diff --git a/chrome/browser/ui/webui/ash/settings/pages/apps/apps_section.cc b/chrome/browser/ui/webui/ash/settings/pages/apps/apps_section.cc
index d92c000..b45feadb 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/apps/apps_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/apps/apps_section.cc
@@ -406,6 +406,18 @@
   html_source->AddLocalizedStrings(kLocalizedStrings);
 }
 
+void AddAppParentalControlsStrings(content::WebUIDataSource* html_source) {
+  static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"appParentalControlsTitle", IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_LABEL},
+      {"appParentalControlsSubtitle",
+       IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_SUBLABEL},
+      {"appParentalControlsSetUpButton",
+       IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_SET_UP_BUTTON},
+  };
+
+  html_source->AddLocalizedStrings(kLocalizedStrings);
+}
+
 bool ShowPluginVm(const Profile* profile, const PrefService& pref_service) {
   // Even if not allowed, we still want to show Plugin VM if the VM image is on
   // disk, so that users are still able to delete the image at will.
@@ -514,9 +526,6 @@
       {"enableIsolatedWebAppsToggleLabel",
        IDS_SETTINGS_ENABLE_ISOLATED_WEB_APPS_LABEL},
       {"appManagementAppLanguageLabel", IDS_APP_MANAGEMENT_APP_LANGUAGE_LABEL},
-      {"appParentalControlsTitle", IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_LABEL},
-      {"appParentalControlsSubtitle",
-       IDS_OS_SETTINGS_APP_PARENTAL_CONTROLS_SUBLABEL},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
@@ -564,6 +573,7 @@
   AddAndroidAppStrings(html_source);
   AddPluginVmLoadTimeData(html_source);
   AddBorealisStrings(html_source);
+  AddAppParentalControlsStrings(html_source);
 
   // Startup subsection exists only when OsSettingsRevampWayfinding is disabled.
   if (startup_subsection_) {
diff --git a/chrome/browser/ui/webui/ash/settings/pages/multitasking/multitasking_section.cc b/chrome/browser/ui/webui/ash/settings/pages/multitasking/multitasking_section.cc
index 59b0a40..580083f 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/multitasking/multitasking_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/multitasking/multitasking_section.cc
@@ -88,8 +88,9 @@
   // Note: This is a subsection that exists under System Preferences. This is
   // not a top-level section and does not have a respective declaration in
   // chromeos::settings::mojom::Section.
-  return ShouldShowMultitasking() ? mojom::Section::kSystemPreferences
-                                  : mojom::Section::kPersonalization;
+  return ash::features::IsOsSettingsRevampWayfindingEnabled()
+             ? mojom::Section::kSystemPreferences
+             : mojom::Section::kPersonalization;
 }
 
 mojom::SearchResultIcon MultitaskingSection::GetSectionIcon() const {
@@ -97,8 +98,9 @@
 }
 
 const char* MultitaskingSection::GetSectionPath() const {
-  return ShouldShowMultitasking() ? mojom::kSystemPreferencesSectionPath
-                                  : mojom::kPersonalizationSectionPath;
+  return ash::features::IsOsSettingsRevampWayfindingEnabled()
+             ? mojom::kSystemPreferencesSectionPath
+             : mojom::kPersonalizationSectionPath;
 }
 
 bool MultitaskingSection::LogMetric(mojom::Setting setting,
diff --git a/chrome/browser/ui/webui/ash/settings/pages/personalization/personalization_section.cc b/chrome/browser/ui/webui/ash/settings/pages/personalization/personalization_section.cc
index a9c8bf46..b78bbf67 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/personalization/personalization_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/personalization/personalization_section.cc
@@ -49,7 +49,7 @@
     : OsSettingsSection(profile, search_tag_registry),
       isRevampEnabled_(ash::features::IsOsSettingsRevampWayfindingEnabled()),
       multitasking_subsection_(
-          ShouldShowMultitaskingInPersonalization()
+          !isRevampEnabled_
               ? std::make_optional<MultitaskingSection>(profile,
                                                         search_tag_registry)
               : std::nullopt) {
@@ -83,14 +83,14 @@
   };
 
   html_source->AddLocalizedStrings(kWallpaperLocalizedStrings);
-  if (ShouldShowMultitaskingInPersonalization()) {
+  if (multitasking_subsection_) {
     multitasking_subsection_->AddLoadTimeData(html_source);
   }
 }
 
 void PersonalizationSection::AddHandlers(content::WebUI* web_ui) {
   web_ui->AddMessageHandler(std::make_unique<PersonalizationHubHandler>());
-  if (ShouldShowMultitaskingInPersonalization()) {
+  if (multitasking_subsection_) {
     multitasking_subsection_->AddHandlers(web_ui);
   }
 }
@@ -121,7 +121,7 @@
 void PersonalizationSection::RegisterHierarchy(
     HierarchyGenerator* generator) const {
   generator->RegisterTopLevelSetting(mojom::Setting::kOpenWallpaper);
-  if (ShouldShowMultitaskingInPersonalization()) {
+  if (multitasking_subsection_) {
     multitasking_subsection_->RegisterHierarchy(generator);
   }
 }
diff --git a/chrome/browser/ui/webui/ash/settings/pages/privacy/metrics_consent_handler.cc b/chrome/browser/ui/webui/ash/settings/pages/privacy/metrics_consent_handler.cc
index ab96eb5..41d81125 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/privacy/metrics_consent_handler.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/privacy/metrics_consent_handler.cc
@@ -98,6 +98,13 @@
 }
 
 bool MetricsConsentHandler::IsMetricsConsentConfigurable() const {
+  // TODO(b/333911538): In the interim, completely disable child users
+  // from being able to toggle consent in the settings. Once the parent sets
+  // the consent for the child during OOBE, it cannot be updated afterwards.
+  if (user_manager_->IsLoggedInAsChildUser()) {
+    return false;
+  }
+
   return ShouldUseUserConsent() || user_manager_->IsCurrentUserOwner();
 }
 
diff --git a/chrome/browser/ui/webui/ash/settings/pages/privacy/metrics_consent_handler_unittest.cc b/chrome/browser/ui/webui/ash/settings/pages/privacy/metrics_consent_handler_unittest.cc
index f05fa8c..a369b66 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/privacy/metrics_consent_handler_unittest.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/privacy/metrics_consent_handler_unittest.cc
@@ -394,4 +394,39 @@
   StatsReportingController::Shutdown();
 }
 
+TEST_F(MetricsConsentHandlerTest, ChildUserCannotToggleAsNonOwner) {
+  auto owner_id = AccountId::FromUserEmailGaiaId(kOwner, "2");
+  std::unique_ptr<TestingProfile> owner = RegisterOwner(owner_id);
+
+  auto child_id = AccountId::FromUserEmailGaiaId("child@user.com", "3");
+  std::unique_ptr<TestingProfile> child =
+      CreateUser("child@user.com", non_owner_keys);
+  test_user_manager_->set_current_user_child(true);
+  test_user_manager_->AddUserWithAffiliationAndTypeAndProfile(
+      child_id, false, user_manager::UserType::kChild, child.get());
+
+  // User cannot use user consent. This happens if the device is managed.
+  test_metrics_service_client_->SetShouldUseUserConsent(true);
+
+  LoginUser(child_id);
+  EXPECT_FALSE(test_user_manager_->IsCurrentUserOwner());
+
+  // Set the javascript message object for metrics consent state.
+  InitializeTestHandler(child.get());
+  handler_->GetMetricsConsentState();
+
+  // Check values of javascript callback response message.
+  std::string pref_name;
+  bool is_configurable;
+  EXPECT_TRUE(GetMetricsConsentStateMessage(&pref_name, &is_configurable));
+
+  // Unmanaged child user should use user consent and should not be toggle-able.
+  EXPECT_THAT(pref_name, Eq(::metrics::prefs::kMetricsUserConsent));
+  EXPECT_FALSE(is_configurable);
+
+  // Explicitly shutdown controller here because OwnerSettingsService is
+  // destructed before TearDown() is called.
+  StatsReportingController::Shutdown();
+}
+
 }  // namespace ash::settings
diff --git a/chrome/browser/ui/webui/commerce/product_specifications_ui.cc b/chrome/browser/ui/webui/commerce/product_specifications_ui.cc
index e7ac354..03c19124b 100644
--- a/chrome/browser/ui/webui/commerce/product_specifications_ui.cc
+++ b/chrome/browser/ui/webui/commerce/product_specifications_ui.cc
@@ -17,6 +17,7 @@
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/commerce/core/shopping_service.h"
 #include "components/commerce/core/webui/shopping_service_handler.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -50,6 +51,12 @@
   source->AddString("message", "Some example content...");
   source->AddString("pageTitle", "Product Specifications");
   source->AddString("summaryTitle", "Summary");
+
+  static constexpr webui::LocalizedString kStrings[] = {
+      {"openTabsSectionTitle", IDS_PRODUCT_SPECIFICATIONS_OPEN_TABS_SECTION},
+  };
+
+  source->AddLocalizedStrings(kStrings);
 }
 
 void ProductSpecificationsUI::BindInterface(
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
index 6bca368..89f04e9 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
+++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
@@ -235,6 +235,10 @@
   InstallGoodExtension();
   SafetyHubMenuNotificationService* notification_service =
       SafetyHubMenuNotificationServiceFactory::GetForProfile(profile);
+  // Safety Hub services will be initialized when
+  // SafetyHubMenuNotificationService is created. Let Safety Hub services to
+  // initialize properly.
+  safety_hub_test_util::RunUntilPasswordCheckCompleted(profile);
   // No unpublished extensions yet, so there shouldn't be a menu notifications.
   std::optional<MenuNotificationEntry> notification =
       notification_service->GetNotificationToShow();
diff --git a/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc b/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
index 8c2ab02..fd60a3b 100644
--- a/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_default_browser_handler.cc
@@ -86,7 +86,8 @@
   // them when this changes and close all open prompts.
   DefaultBrowserPromptManager::UpdatePrefsForDismissedPrompt(
       Profile::FromWebUI(web_ui()));
-  DefaultBrowserPromptManager::GetInstance()->CloseAllPrompts();
+  DefaultBrowserPromptManager::GetInstance()->CloseAllPrompts(
+      DefaultBrowserPromptManager::CloseReason::kDismiss);
 }
 
 void DefaultBrowserHandler::OnDefaultBrowserSettingChange() {
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 0c2fbf18..ef596c7 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -712,6 +712,7 @@
     "//content/test:test_support",
     "//services/data_decoder/public/cpp:test_support",
     "//testing/gtest",
+    "//ui/gfx:test_support",
     "//ui/webui",
   ]
   if (is_chromeos_ash) {
diff --git a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
index 449bf07c..4c021cd 100644
--- a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
@@ -56,6 +56,7 @@
 #include "ui/color/color_provider_utils.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/test/sk_gmock_support.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/components/arc/mojom/intent_helper.mojom.h"
@@ -71,54 +72,6 @@
 namespace web_app {
 namespace {
 
-MATCHER_P(EqualsBitmap, expected_bmp, "") {
-  // Number of pixels with an error
-  int error_pixels_count = 0;
-
-  gfx::Rect error_bounding_rect = gfx::Rect();
-
-  // Check that bitmaps have identical dimensions.
-  if (arg.width() != expected_bmp.width()) {
-    *result_listener << "where widths do not match, actual: " << arg.width()
-                     << ", expected: " << expected_bmp.width();
-    return false;
-  }
-  if (arg.height() != expected_bmp.height()) {
-    *result_listener << "where heights do not match, actual: " << arg.height()
-                     << ", expected: " << expected_bmp.height();
-    return false;
-  }
-
-  for (int x = 0; x < arg.width(); ++x) {
-    for (int y = 0; y < arg.height(); ++y) {
-      SkColor actual_color = arg.getColor(x, y);
-      SkColor expected_color = expected_bmp.getColor(x, y);
-      if (actual_color != expected_color) {
-        ++error_pixels_count;
-        error_bounding_rect.Union(gfx::Rect(x, y, 1, 1));
-      }
-    }
-  }
-
-  if (error_pixels_count != 0) {
-    *result_listener << "Number of pixel with an error: " << error_pixels_count
-                     << "\nError Bounding Box : "
-                     << error_bounding_rect.ToString() << "\n";
-    int sample_x = expected_bmp.width() / 2;
-    int sample_y = expected_bmp.height() / 2;
-    std::string expected_color = color_utils::SkColorToRgbaString(
-        expected_bmp.getColor(sample_x, sample_y));
-    std::string actual_color =
-        color_utils::SkColorToRgbaString(arg.getColor(sample_x, sample_y));
-    *result_listener << "Sample pixel comparison at " << sample_x << "x"
-                     << sample_y << ": Expected " << expected_color
-                     << ", actual " << actual_color;
-    return false;
-  }
-
-  return true;
-}
-
 class FetchManifestAndInstallCommandTest : public WebAppTest {
  public:
   const GURL kWebAppUrl = GURL("https://example.com/path/index.html");
@@ -1044,7 +997,7 @@
   ASSERT_TRUE(icons_future.Wait());
   std::map<SquareSizePx, SkBitmap> bitmaps = icons_future.Get();
   EXPECT_THAT(bitmaps[icon_size::k256],
-              EqualsBitmap(GenerateExpected256Icon()));
+              gfx::test::EqualsBitmap(GenerateExpected256Icon()));
 
   EXPECT_EQ(IsDiyApp(), provider()->registrar_unsafe().IsDiyApp(app_id));
 
diff --git a/chrome/browser/web_applications/commands/install_from_info_command_browsertest.cc b/chrome/browser/web_applications/commands/install_from_info_command_browsertest.cc
index 0ef5881..fcc99c0 100644
--- a/chrome/browser/web_applications/commands/install_from_info_command_browsertest.cc
+++ b/chrome/browser/web_applications/commands/install_from_info_command_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 "install_from_info_command.h"
+#include "chrome/browser/web_applications/commands/install_from_info_command.h"
 
 #include <map>
 #include <memory>
@@ -11,6 +11,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/test_future.h"
 #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "chrome/browser/web_applications/test/fake_os_integration_manager.h"
@@ -18,6 +19,7 @@
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
+#include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_icon_manager.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -31,14 +33,7 @@
 
 class InstallFromInfoCommandTest : public WebAppBrowserTestBase {
  public:
-  InstallFromInfoCommandTest() {
-    WebAppProvider::SetOsIntegrationManagerFactoryForTesting(
-        [](Profile* profile) -> std::unique_ptr<OsIntegrationManager> {
-          return std::make_unique<FakeOsIntegrationManager>(profile, nullptr,
-                                                            nullptr, nullptr);
-        });
-  }
-
+  InstallFromInfoCommandTest() = default;
   std::map<SquareSizePx, SkBitmap> ReadIcons(const webapps::AppId& app_id,
                                              IconPurpose purpose,
                                              const SortedSizesPx& sizes_px) {
@@ -54,10 +49,6 @@
     run_loop.Run();
     return result;
   }
-
-  FakeOsIntegrationManager* os_integration_manager() {
-    return provider().os_integration_manager().AsTestOsIntegrationManager();
-  }
 };
 
 IN_PROC_BROWSER_TEST_F(InstallFromInfoCommandTest, SuccessInstall) {
diff --git a/chrome/browser/web_applications/commands/launch_web_app_command.cc b/chrome/browser/web_applications/commands/launch_web_app_command.cc
index 4c16c45..d439f87 100644
--- a/chrome/browser/web_applications/commands/launch_web_app_command.cc
+++ b/chrome/browser/web_applications/commands/launch_web_app_command.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/commands/web_app_command.h"
 #include "chrome/browser/web_applications/locks/app_lock.h"
+#include "chrome/browser/web_applications/os_integration/os_integration_test_override.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_ui_manager.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
@@ -59,6 +60,20 @@
     return;
   }
 
+  bool is_standalone_launch =
+      params_.container == apps::LaunchContainer::kLaunchContainerWindow ||
+      (launch_setting_ ==
+           LaunchWebAppWindowSetting::kOverrideWithWebAppConfig &&
+       lock_->registrar().GetAppUserDisplayMode(params_.app_id) !=
+           mojom::UserDisplayMode::kBrowser);
+
+  if (is_standalone_launch) {
+    // Launching an app in a standalone windows requires OS integration, and the
+    // only way this is supported in tests is to use the
+    // OsIntegrationTestOverride functionality.
+    CHECK_OS_INTEGRATION_ALLOWED();
+  }
+
   provider_->ui_manager().LaunchWebApp(
       std::move(params_), launch_setting_, *profile_,
       base::BindOnce(&LaunchWebAppCommand::OnAppLaunched,
diff --git a/chrome/browser/web_applications/extensions/web_app_uninstall_and_replace_job.cc b/chrome/browser/web_applications/extensions/web_app_uninstall_and_replace_job.cc
index 132f4ec..6075866 100644
--- a/chrome/browser/web_applications/extensions/web_app_uninstall_and_replace_job.cc
+++ b/chrome/browser/web_applications/extensions/web_app_uninstall_and_replace_job.cc
@@ -80,7 +80,7 @@
 WebAppUninstallAndReplaceJob::~WebAppUninstallAndReplaceJob() = default;
 
 void WebAppUninstallAndReplaceJob::Start() {
-  DCHECK(to_app_lock_->registrar().IsInstalled(to_app_));
+  CHECK(to_app_lock_->registrar().GetAppById(to_app_));
 
   std::vector<webapps::AppId> apps_to_replace;
   for (const webapps::AppId& from_app : from_apps_or_extensions_) {
@@ -184,20 +184,29 @@
 void WebAppUninstallAndReplaceJob::OnShortcutLocationGathered(
     const webapps::AppId& from_app,
     base::OnceClosure on_complete,
-    ShortcutLocations locations) {
+    ShortcutLocations from_app_locations) {
   auto* proxy = apps::AppServiceProxyFactory::GetForProfile(&profile_.get());
 
   const bool is_extension = proxy->AppRegistryCache().GetAppType(from_app) ==
                             apps::AppType::kChromeApp;
+  bool run_on_os_login = from_app_locations.in_startup;
   if (is_extension) {
     // Need to be called before `proxy->UninstallSilently` because
     // UninstallSilently might synchronously finish, so the wait won't get
     // finished if called after.
     WaitForExtensionShortcutsDeleted(
-        from_app, base::BindOnce(&WebAppUninstallAndReplaceJob::
-                                     SynchronizeOSIntegrationForReplacementApp,
-                                 weak_ptr_factory_.GetWeakPtr(),
-                                 std::move(on_complete), locations));
+        from_app,
+        base::BindOnce(&WebAppUninstallAndReplaceJob::
+                           SynchronizeOSIntegrationForReplacementApp,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(on_complete),
+                       run_on_os_login, from_app_locations));
+  } else {
+    // Platforms like Mac don't fetch the 'run on os login' property from the
+    // GetAppExistingShortCutLocation API.
+    run_on_os_login =
+        run_on_os_login ||
+        to_app_lock_->registrar().GetAppRunOnOsLoginMode(from_app).value ==
+            RunOnOsLoginMode::kWindowed;
   }
 
   // When the `from_app` is a web app, we can't wait for it to finish because it
@@ -207,18 +216,19 @@
   proxy->UninstallSilently(from_app, apps::UninstallSource::kMigration);
 
   if (!is_extension) {
-    SynchronizeOSIntegrationForReplacementApp(std::move(on_complete),
-                                              locations);
+    SynchronizeOSIntegrationForReplacementApp(
+        std::move(on_complete), run_on_os_login, from_app_locations);
   }
 }
 
 void WebAppUninstallAndReplaceJob::SynchronizeOSIntegrationForReplacementApp(
     base::OnceClosure on_complete,
-    ShortcutLocations locations) {
+    bool from_app_run_on_os_login,
+    ShortcutLocations from_app_locations) {
   ValueWithPolicy<RunOnOsLoginMode> run_on_os_login =
       to_app_lock_->registrar().GetAppRunOnOsLoginMode(to_app_);
   if (run_on_os_login.user_controllable) {
-    RunOnOsLoginMode new_mode = locations.in_startup
+    RunOnOsLoginMode new_mode = from_app_run_on_os_login
                                     ? RunOnOsLoginMode::kWindowed
                                     : RunOnOsLoginMode::kNotRun;
     if (new_mode != run_on_os_login.value) {
@@ -230,8 +240,9 @@
   }
 
   SynchronizeOsOptions synchronize_options;
-  synchronize_options.add_shortcut_to_desktop = locations.on_desktop;
-  synchronize_options.add_to_quick_launch_bar = locations.in_quick_launch_bar;
+  synchronize_options.add_shortcut_to_desktop = from_app_locations.on_desktop;
+  synchronize_options.add_to_quick_launch_bar =
+      from_app_locations.in_quick_launch_bar;
   synchronize_options.reason = SHORTCUT_CREATION_AUTOMATED;
   to_app_lock_->os_integration_manager().Synchronize(to_app_,
                                                      std::move(on_complete));
diff --git a/chrome/browser/web_applications/jobs/uninstall/web_app_uninstall_and_replace_job.h b/chrome/browser/web_applications/jobs/uninstall/web_app_uninstall_and_replace_job.h
index dcbb0c1..7e2e41b 100644
--- a/chrome/browser/web_applications/jobs/uninstall/web_app_uninstall_and_replace_job.h
+++ b/chrome/browser/web_applications/jobs/uninstall/web_app_uninstall_and_replace_job.h
@@ -54,10 +54,12 @@
 
   void OnShortcutLocationGathered(const webapps::AppId& from_app,
                                   base::OnceClosure on_complete,
-                                  ShortcutLocations locations);
+                                  ShortcutLocations from_app_locations);
 
-  void SynchronizeOSIntegrationForReplacementApp(base::OnceClosure on_complete,
-                                                 ShortcutLocations locations);
+  void SynchronizeOSIntegrationForReplacementApp(
+      base::OnceClosure on_complete,
+      bool from_app_run_on_os_login,
+      ShortcutLocations from_app_locations);
 
   const raw_ref<Profile> profile_;
   const raw_ref<base::Value::Dict> debug_value_;
diff --git a/chrome/browser/web_applications/os_integration/os_integration_manager.cc b/chrome/browser/web_applications/os_integration/os_integration_manager.cc
index af4f2ce5..71e0d080 100644
--- a/chrome/browser/web_applications/os_integration/os_integration_manager.cc
+++ b/chrome/browser/web_applications/os_integration/os_integration_manager.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/os_integration/file_handling_sub_manager.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_sub_manager.h"
+#include "chrome/browser/web_applications/os_integration/os_integration_test_override.h"
 #include "chrome/browser/web_applications/os_integration/protocol_handling_sub_manager.h"
 #include "chrome/browser/web_applications/os_integration/run_on_os_login_sub_manager.h"
 #include "chrome/browser/web_applications/os_integration/shortcut_menu_handling_sub_manager.h"
@@ -144,6 +145,7 @@
   // This is usually called to clean up OS integration states on the OS,
   // regardless of whether there are apps existing in the app registry or not.
   if (options.has_value() && options.value().force_unregister_os_integration) {
+    CHECK_OS_INTEGRATION_ALLOWED();
     ForceUnregisterOsIntegrationOnSubManager(
         app_id, /*index=*/0,
         std::move(callback).Then(
@@ -308,6 +310,8 @@
     return;
   }
 
+  CHECK_OS_INTEGRATION_ALLOWED();
+
   ExecuteNextSubmanager(app_id, options, desired_states_ptr,
                         web_app->current_os_integration_states(), /*index=*/0,
                         std::move(write_state_to_db));
diff --git a/chrome/browser/web_applications/os_integration/os_integration_test_override.cc b/chrome/browser/web_applications/os_integration/os_integration_test_override.cc
index ab5bb83..befd9553 100644
--- a/chrome/browser/web_applications/os_integration/os_integration_test_override.cc
+++ b/chrome/browser/web_applications/os_integration/os_integration_test_override.cc
@@ -14,6 +14,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
+#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "components/webapps/common/web_app_id.h"
 
 namespace web_app {
@@ -35,6 +36,27 @@
 }  // namespace
 
 // static
+void OsIntegrationTestOverride::CheckOsIntegrationAllowed() {
+#if !BUILDFLAG(IS_CHROMEOS)
+  // Note: Using OsIntegrationManager::SuppressForTesting disables os
+  // integration, even if OsIntegrationTestOverride is specified. In this case,
+  // os integration is still not allowed, and anything needing it (like
+  // launching) should call this function & check-fail here.
+  bool os_integration_can_occur_in_tests =
+      !OsIntegrationManager::AreOsHooksSuppressedForTesting() &&
+      ::web_app::OsIntegrationTestOverride::Get();
+  if (!os_integration_can_occur_in_tests) {
+    CHECK_IS_NOT_TEST()
+        << "Please initialize an "
+           "`OsIntegrationTestOverrideBlockingRegistration`"
+           "to allow fully installed web apps with OS integration in tests. In "
+           "unit tests it may be required to call "
+           "`FakeWebAppProvider::UseRealOsIntegrationManager()` during set up.";
+  }
+#endif
+}
+
+// static
 scoped_refptr<OsIntegrationTestOverride> OsIntegrationTestOverride::Get() {
   auto& state = GetMutableOsIntegrationTestOverrideStateForTesting();
   base::AutoLock state_lock(state.lock);
diff --git a/chrome/browser/web_applications/os_integration/os_integration_test_override.h b/chrome/browser/web_applications/os_integration/os_integration_test_override.h
index c4563a4..a13dd701 100644
--- a/chrome/browser/web_applications/os_integration/os_integration_test_override.h
+++ b/chrome/browser/web_applications/os_integration/os_integration_test_override.h
@@ -66,6 +66,8 @@
 class OsIntegrationTestOverride
     : public base::RefCountedThreadSafe<OsIntegrationTestOverride> {
  public:
+  static void CheckOsIntegrationAllowed();
+
   // This will return a nullptr in production code or tests that have not
   // created a `OsIntegrationTestOverrideImpl::BlockingRegistration` through
   // `OsIntegrationTestOverrideImpl::OverrideForTesting`.
@@ -130,4 +132,7 @@
 
 }  // namespace web_app
 
+#define CHECK_OS_INTEGRATION_ALLOWED() \
+  OsIntegrationTestOverride::CheckOsIntegrationAllowed()
+
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_OS_INTEGRATION_OS_INTEGRATION_TEST_OVERRIDE_H_
diff --git a/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc b/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
index 9bd0d60..745482dd 100644
--- a/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
@@ -39,11 +39,6 @@
             PreinstalledWebAppManager::SkipStartupForTesting()) {
     // Ignore any default app configs on disk.
     SetPreinstalledWebAppConfigDirForTesting(&empty_path_);
-    WebAppProvider::SetOsIntegrationManagerFactoryForTesting(
-        [](Profile* profile) -> std::unique_ptr<OsIntegrationManager> {
-          return std::make_unique<FakeOsIntegrationManager>(profile, nullptr,
-                                                            nullptr, nullptr);
-        });
   }
 
   ~PreinstalledWebAppsBrowserTest() override {
diff --git a/chrome/browser/web_applications/test/fake_web_app_provider.cc b/chrome/browser/web_applications/test/fake_web_app_provider.cc
index cad06b8..040e25e 100644
--- a/chrome/browser/web_applications/test/fake_web_app_provider.cc
+++ b/chrome/browser/web_applications/test/fake_web_app_provider.cc
@@ -98,6 +98,20 @@
   synchronize_preinstalled_app_on_startup_ = synchronize_on_startup;
 }
 
+void FakeWebAppProvider::UseRealOsIntegrationManager() {
+  CheckNotStartedAndDisconnect();
+  auto file_handler_manager =
+      std::make_unique<WebAppFileHandlerManager>(profile_);
+  auto protocol_handler_manager =
+      std::make_unique<WebAppProtocolHandlerManager>(profile_);
+  auto shortcut_manager = std::make_unique<WebAppShortcutManager>(
+      profile_, file_handler_manager.get(), protocol_handler_manager.get());
+
+  SetOsIntegrationManager(std::make_unique<OsIntegrationManager>(
+      profile_, std::move(shortcut_manager), std::move(file_handler_manager),
+      std::move(protocol_handler_manager)));
+}
+
 void FakeWebAppProvider::SetEnableAutomaticIwaUpdates(
     AutomaticIwaUpdateStrategy automatic_iwa_update_strategy) {
   CheckNotStartedAndDisconnect();
diff --git a/chrome/browser/web_applications/test/fake_web_app_provider.h b/chrome/browser/web_applications/test/fake_web_app_provider.h
index 3739fdf..2d28bf29 100644
--- a/chrome/browser/web_applications/test/fake_web_app_provider.h
+++ b/chrome/browser/web_applications/test/fake_web_app_provider.h
@@ -108,6 +108,11 @@
   // by default for unit tests, and can be enabled by setting this flag to true.
   void SetSynchronizePreinstalledAppsOnStartup(bool synchronize_on_startup);
 
+  // Call when the unit tets wants to trigger OS integration, removing the
+  // ScopedSuppressOsHooks in FakeOsIntegrationManager (allowing the
+  // OsIntegrationTestOverrideBlockingRegistration to work correctly).
+  void UseRealOsIntegrationManager();
+
   enum class AutomaticIwaUpdateStrategy {
     kDefault,
     kForceDisabled,
diff --git a/chrome/browser/web_applications/test/os_integration_test_override_impl.h b/chrome/browser/web_applications/test/os_integration_test_override_impl.h
index 97ed1e35..215b1c7 100644
--- a/chrome/browser/web_applications/test/os_integration_test_override_impl.h
+++ b/chrome/browser/web_applications/test/os_integration_test_override_impl.h
@@ -57,6 +57,12 @@
 // integration (disk folders, windows registry changes, etc) have been removed.
 //
 // `test_override()` can be used to view or modify the OS state.
+//
+// Note: This override does not apply if there is a
+// OsIntegrationManager::ScopedSuppressForTesting is created. This often happens
+// in unit tests, which use a FakeWebAppProvider by default. To reset that
+// object held by the FakeOsIntegrationManager, call
+// FakeWebAppProvider::UseRealOsIntegrationManager() on test setup.
 class OsIntegrationTestOverrideBlockingRegistration {
  public:
   OsIntegrationTestOverrideBlockingRegistration();
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc
index 13f2633..981bfa1 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.cc
+++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -402,6 +402,19 @@
       location);
 }
 
+void WebAppCommandScheduler::RemoveAllManagementTypesAndUninstall(
+    base::PassKey<WebAppSyncBridge>,
+    const webapps::AppId& app_id,
+    webapps::WebappUninstallSource uninstall_source,
+    UninstallJob::Callback callback,
+    const base::Location& location) {
+  provider_->command_manager().ScheduleCommand(
+      WebAppUninstallCommand::CreateForRemoveInstallManagements(
+          uninstall_source, *profile_, app_id, WebAppManagementTypes::All(),
+          std::move(callback)),
+      location);
+}
+
 void WebAppCommandScheduler::UninstallAllUserInstalledWebApps(
     webapps::WebappUninstallSource uninstall_source,
     UninstallAllUserInstalledWebAppsCommand::Callback callback,
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h
index aeb4abdb..d6c8e64f 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.h
+++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -301,6 +301,21 @@
       UninstallAllUserInstalledWebAppsCommand::Callback callback,
       const base::Location& location = FROM_HERE);
 
+  // Completely removes the web_app from the database by removing all management
+  // types. Since this is a very destructive operation, prefer invoking
+  // RemoveInstallUrlMaybeUninstall(), RemoveInstallManagementMaybeUninstall(),
+  // RemoveUserUninstallableManagements() or UninstallAllUserInstalledWebApps()
+  // instead.
+  // Currently, only the WebAppSyncBridge is allowed to invoke this for
+  // uninstalling web apps, since it is safe to assume that apps marked with
+  // `is_uninstalling` set to true can be fully removed from the registry.
+  void RemoveAllManagementTypesAndUninstall(
+      base::PassKey<WebAppSyncBridge>,
+      const webapps::AppId& app_id,
+      webapps::WebappUninstallSource uninstall_source,
+      UninstallJob::Callback callback,
+      const base::Location& location = FROM_HERE);
+
   // Schedules a command that updates run on os login to provided `login_mode`
   // for a web app.
   void SetRunOnOsLoginMode(const webapps::AppId& app_id,
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index 1091966d..4b1c17a 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -73,13 +73,6 @@
 
 namespace web_app {
 
-namespace {
-
-WebAppProvider::OsIntegrationManagerFactory
-    g_os_integration_manager_factory_for_testing = nullptr;
-
-}  // namespace
-
 // static
 WebAppProvider* WebAppProvider::GetDeprecated(Profile* profile) {
   return WebAppProviderFactory::GetForProfile(profile);
@@ -136,12 +129,6 @@
   return WebAppProvider::GetForLocalAppsUnchecked(profile);
 }
 
-// static
-void WebAppProvider::SetOsIntegrationManagerFactoryForTesting(
-    OsIntegrationManagerFactory factory) {
-  g_os_integration_manager_factory_for_testing = factory;
-}
-
 WebAppProvider::WebAppProvider(Profile* profile) : profile_(profile) {
   DCHECK(AreWebAppsEnabled(profile_));
 
@@ -360,21 +347,16 @@
   translation_manager_ = std::make_unique<WebAppTranslationManager>(profile);
   install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(profile);
 
-  if (g_os_integration_manager_factory_for_testing) {
-    os_integration_manager_ =
-        g_os_integration_manager_factory_for_testing(profile);
-  } else {
-    auto file_handler_manager =
-        std::make_unique<WebAppFileHandlerManager>(profile);
-    auto protocol_handler_manager =
-        std::make_unique<WebAppProtocolHandlerManager>(profile);
-    auto shortcut_manager = std::make_unique<WebAppShortcutManager>(
-        profile, file_handler_manager.get(), protocol_handler_manager.get());
+  auto file_handler_manager =
+      std::make_unique<WebAppFileHandlerManager>(profile);
+  auto protocol_handler_manager =
+      std::make_unique<WebAppProtocolHandlerManager>(profile);
+  auto shortcut_manager = std::make_unique<WebAppShortcutManager>(
+      profile, file_handler_manager.get(), protocol_handler_manager.get());
 
-    os_integration_manager_ = std::make_unique<OsIntegrationManager>(
-        profile, std::move(shortcut_manager), std::move(file_handler_manager),
-        std::move(protocol_handler_manager));
-  }
+  os_integration_manager_ = std::make_unique<OsIntegrationManager>(
+      profile, std::move(shortcut_manager), std::move(file_handler_manager),
+      std::move(protocol_handler_manager));
 
   command_manager_ = std::make_unique<WebAppCommandManager>(profile);
   command_scheduler_ = std::make_unique<WebAppCommandScheduler>(*profile);
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index 4d97bd8a..635e7a60 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -105,8 +105,6 @@
 
   using OsIntegrationManagerFactory =
       std::unique_ptr<OsIntegrationManager> (*)(Profile*);
-  static void SetOsIntegrationManagerFactoryForTesting(
-      OsIntegrationManagerFactory factory);
 
   explicit WebAppProvider(Profile* profile);
   WebAppProvider(const WebAppProvider&) = delete;
diff --git a/chrome/browser/web_applications/web_app_sync_bridge.cc b/chrome/browser/web_applications/web_app_sync_bridge.cc
index 988bfe3..d0f7de36 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge.cc
+++ b/chrome/browser/web_applications/web_app_sync_bridge.cc
@@ -763,8 +763,9 @@
           apps_to_delete, callback);
     } else {
       for (const webapps::AppId& app_id : apps_to_delete) {
-        command_scheduler_->RemoveUserUninstallableManagements(
-            app_id, webapps::WebappUninstallSource::kSync,
+        command_scheduler_->RemoveAllManagementTypesAndUninstall(
+            base::PassKey<WebAppSyncBridge>(), app_id,
+            webapps::WebappUninstallSource::kSync,
             base::BindOnce(callback, app_id));
       }
     }
@@ -960,8 +961,9 @@
         base::BindRepeating(&WebAppSyncBridge::OnWebAppUninstallComplete,
                             weak_ptr_factory_.GetWeakPtr());
     for (const auto& app_id : apps_uninstalling) {
-      command_scheduler_->RemoveUserUninstallableManagements(
-          app_id, webapps::WebappUninstallSource::kSync,
+      command_scheduler_->RemoveAllManagementTypesAndUninstall(
+          base::PassKey<WebAppSyncBridge>(), app_id,
+          webapps::WebappUninstallSource::kSync,
           base::BindOnce(callback, app_id));
     }
   }
diff --git a/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc b/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc
index 2b78800d..934eb43 100644
--- a/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc
+++ b/chrome/browser/web_applications/web_app_sync_bridge_unittest.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/web_applications/test/web_app_test_observers.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
-#include "chrome/browser/web_applications/web_app_command_manager.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
@@ -1250,6 +1249,45 @@
   run_loop.Run();
 }
 
+// Tests that non user installable apps can also be removed by the
+// WebAppSyncBridge during system startup, if `is_uninstalling` is set to true.
+// Test for crbug.com/335253048, by using kSystem to mock that behavior. Since
+// System Web Apps are only on Ash chrome, kPolicy is used instead on Lacro
+TEST_F(WebAppSyncBridgeTest, CanDeleteNonUserInstallableApps) {
+  AppsList system_apps;
+
+  // This app should be uninstalled, since the `is_uninstalling` field is set.
+  std::unique_ptr<WebApp> app1 =
+      test::CreateWebApp(GURL("https://example.com/app1"));
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  app1->AddSource(WebAppManagement::kPolicy);
+#else
+  app1->AddSource(WebAppManagement::kSystem);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  app1->SetIsUninstalling(/*is_uninstalling=*/true);
+  const webapps::AppId app_id1 = app1->app_id();
+  system_apps.push_back(std::move(app1));
+
+  // This app will not be uninstalled.
+  std::unique_ptr<WebApp> app2 =
+      test::CreateWebApp(GURL("https://example.com/app2"));
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  app2->AddSource(WebAppManagement::kPolicy);
+#else
+  app2->AddSource(WebAppManagement::kSystem);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  const webapps::AppId app_id2 = app2->app_id();
+  system_apps.push_back(std::move(app2));
+
+  Registry registry;
+  InsertAppsListIntoRegistry(&registry, system_apps);
+  database_factory().WriteRegistry(registry);
+  StartWebAppProvider();
+
+  EXPECT_FALSE(registrar().IsInstalled(app_id1));
+  EXPECT_TRUE(registrar().IsInstalled(app_id2));
+}
+
 // Tests that OnWebAppsWillBeUpdatedFromSync observer notification is called
 // properly.
 TEST_F(WebAppSyncBridgeTest,
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 49a1107..af497f7 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1713355171-b44ad20149199537d2c8265dac0605bd4e2d6490-3b980d0bd50a84f848f2bf420646bf9fbcbb1b98.profdata
+chrome-linux-main-1713376773-7b0566891c51886cbdb77315dbb14db01a5e90e1-8cc1a248d1d7e9332208a6174ed0d44ee621affa.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 31d97cf..3c8083b9 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1713333322-40f5810d369cb8ed952a8867c60608f525e99db5-7bc4966503946ba325f1e1423e17fad345759c10.profdata
+chrome-mac-main-1713376773-3408893c430a4f941667222519dd285658ce099e-8cc1a248d1d7e9332208a6174ed0d44ee621affa.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 51688a0..060ca64 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1713355171-2ec78fd32eba8596f40d3b05141495c93d5f5af1-3b980d0bd50a84f848f2bf420646bf9fbcbb1b98.profdata
+chrome-win-arm64-main-1713376773-4bbfeea1beeb6913c2d073d967ed0fb6ade09989-8cc1a248d1d7e9332208a6174ed0d44ee621affa.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 8fcaa98f..885be35c 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1713355171-82a021371f8f4f2b85f42da8f96dd684d5ddd892-3b980d0bd50a84f848f2bf420646bf9fbcbb1b98.profdata
+chrome-win32-main-1713376773-0a2138f96e5d869c1903f37004a43d6ebfd3852e-8cc1a248d1d7e9332208a6174ed0d44ee621affa.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4fa9505..b9ee36b 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1713365905-e9647723fe3c51687f256483b2c666efaa8b5e31-e4b2169c4cc286f2c8970f87af0ae945e2637f09.profdata
+chrome-win64-main-1713376773-ca688a73be5affd1e7003c753db25cbe2adae9e3-8cc1a248d1d7e9332208a6174ed0d44ee621affa.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index ba19af5..84d80b7 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -1119,7 +1119,7 @@
 #endif
 
 // Enables Safety Hub feature.
-BASE_FEATURE(kSafetyHub, "SafetyHub", base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kSafetyHub, "SafetyHub", base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Time between automated runs of the password check.
 const base::FeatureParam<base::TimeDelta> kBackgroundPasswordCheckInterval{
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index c9f6f627..fc58798b 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -22,11 +22,13 @@
 #include "components/translate/core/common/translate_constants.h"
 #include "content/public/renderer/chrome_object_extensions_utils.h"
 #include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_thread.h"
 #include "gin/converter.h"
 #include "gin/dictionary.h"
 #include "gin/handle.h"
 #include "gin/object_template_builder.h"
-#include "read_anything_app_controller.h"
+#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
 #include "third_party/blink/public/web/web_local_frame.h"
@@ -380,9 +382,16 @@
   distiller_ = std::make_unique<AXTreeDistiller>(
       base::BindRepeating(&ReadAnythingAppController::OnAXTreeDistilled,
                           weak_ptr_factory_.GetWeakPtr()));
+  // TODO(crbug.com/1450930): Use a global ukm recorder instance instead.
+  mojo::Remote<ukm::mojom::UkmRecorderFactory> factory;
+  content::RenderThread::Get()->BindHostReceiver(
+      factory.BindNewPipeAndPassReceiver());
+  ukm_recorder_ = ukm::MojoUkmRecorder::Create(*factory);
 }
 
-ReadAnythingAppController::~ReadAnythingAppController() = default;
+ReadAnythingAppController::~ReadAnythingAppController() {
+  RecordNumSelections();
+}
 
 void ReadAnythingAppController::AccessibilityEventReceived(
     const ui::AXTreeID& tree_id,
@@ -433,8 +442,9 @@
   if (tree_id == model_.active_tree_id() && !is_pdf) {
     return;
   }
+  RecordNumSelections();
   model_.set_active_tree_id(tree_id);
-  model_.SetActiveUkmSourceId(ukm_source_id);
+  model_.set_ukm_source_id(ukm_source_id);
   model_.set_is_pdf(is_pdf);
   // Delete all pending updates on the formerly active AXTree.
   // TODO(crbug.com/1266555): If distillation is in progress, cancel the
@@ -454,6 +464,14 @@
   }
 }
 
+void ReadAnythingAppController::RecordNumSelections() {
+  ukm::builders::Accessibility_ReadAnything_EmptyState(
+      model_.ukm_source_id())
+      .SetTotalNumSelections(model_.num_selections())
+      .Record(ukm_recorder_.get());
+  model_.set_num_selections(0);
+}
+
 void ReadAnythingAppController::OnAXTreeDestroyed(const ui::AXTreeID& tree_id) {
   model_.OnAXTreeDestroyed(tree_id);
 }
@@ -491,7 +509,7 @@
   }
   CHECK(serializer.SerializeChanges(tree->root(), &snapshot));
   model_.SetDistillationInProgress(true);
-  distiller_->Distill(*tree, snapshot, model_.active_ukm_source_id());
+  distiller_->Distill(*tree, snapshot, model_.ukm_source_id());
 }
 
 void ReadAnythingAppController::OnAXTreeDistilled(
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything_app_controller.h
index 6ceb62bd..5757341 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything_app_controller.h
@@ -36,6 +36,10 @@
 class AXTree;
 }  // namespace ui
 
+namespace ukm {
+class MojoUkmRecorder;
+}  // namespace ukm
+
 class AXTreeDistiller;
 class ReadAnythingAppControllerTest;
 
@@ -261,6 +265,10 @@
   // node isn't in the current segment.
   int GetCurrentTextEndIndex(ui::AXNodeID node_id);
 
+  // Records the number of selections that occurred for the active page. Called
+  // when the active tree changes.
+  void RecordNumSelections();
+
   // SetContentForTesting, SetThemeForTesting, and SetLanguageForTesting are
   // used by ReadAnythingAppTest and thus need to be kept in
   // ReadAnythingAppController even though ReadAnythingAppControllerBrowserTest
@@ -310,6 +318,8 @@
 
   // For metrics logging
 
+  std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder_;
+
   // The time when the renderer constructor is first triggered.
   base::TimeTicks renderer_load_triggered_time_ms_;
 
diff --git a/chrome/renderer/accessibility/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything_app_model.cc
index 48a5d88..eaca3b73 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything_app_model.cc
@@ -10,8 +10,6 @@
 #include "base/containers/contains.h"
 #include "base/metrics/histogram_functions.h"
 #include "content/public/renderer/render_thread.h"
-#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/accessibility/ax_enum_util.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
@@ -21,6 +19,7 @@
 #include "ui/accessibility/ax_serializable_tree.h"
 #include "ui/accessibility/ax_text_utils.h"
 #include "ui/accessibility/ax_tree_update_util.h"
+#include "url/gurl.h"
 
 namespace {
 
@@ -44,17 +43,9 @@
 
 }  // namespace
 
-ReadAnythingAppModel::ReadAnythingAppModel() {
-  // TODO(crbug.com/1450930): Use a global ukm recorder instance instead.
-  mojo::Remote<ukm::mojom::UkmRecorderFactory> factory;
-  content::RenderThread::Get()->BindHostReceiver(
-      factory.BindNewPipeAndPassReceiver());
-  ukm_recorder_ = ukm::MojoUkmRecorder::Create(*factory);
-}
+ReadAnythingAppModel::ReadAnythingAppModel() = default;
 
-ReadAnythingAppModel::~ReadAnythingAppModel() {
-  SetActiveUkmSourceId(ukm::kInvalidSourceId);
-}
+ReadAnythingAppModel::~ReadAnythingAppModel() = default;
 
 ReadAnythingAppModel::AXTreeInfo::AXTreeInfo(
     std::unique_ptr<ui::AXTreeManager> other) {
@@ -161,7 +152,7 @@
     base::UmaHistogramEnumeration(
         string_constants::kEmptyStateHistogramName,
         ReadAnythingEmptyState::kSelectionAfterEmptyStateShown);
-    num_selections_++;
+    tree_infos_.at(active_tree_id_)->num_selections++;
   }
 
   // If the main panel selection contains content outside of the distilled
@@ -579,23 +570,61 @@
     // TODO(crbug.com/1266555): If distillation is in progress, cancel the
     // distillation request.
     active_tree_id_ = ui::AXTreeIDUnknown();
-    SetActiveUkmSourceId(ukm::kInvalidSourceId);
+    set_ukm_source_id(ukm::kInvalidSourceId);
   }
   EraseTree(tree_id);
 }
 
-void ReadAnythingAppModel::SetActiveUkmSourceId(
-    const ukm::SourceId& source_id) {
-  // Record the number of selections made on the current page if it was not
-  // distillable.
-  if (active_ukm_source_id_ != ukm::kInvalidSourceId &&
-      content_node_ids_.empty()) {
-    ukm::builders::Accessibility_ReadAnything_EmptyState(active_ukm_source_id_)
-        .SetTotalNumSelections(num_selections_)
-        .Record(ukm_recorder_.get());
+const ukm::SourceId& ReadAnythingAppModel::ukm_source_id() {
+  if (base::Contains(tree_infos_, active_tree_id_)) {
+    ReadAnythingAppModel::AXTreeInfo* tree_info =
+        tree_infos_.at(active_tree_id_).get();
+    if (tree_info) {
+      return tree_info->ukm_source_id;
+    }
   }
-  num_selections_ = 0;
-  active_ukm_source_id_ = source_id;
+  return ukm::kInvalidSourceId;
+}
+
+void ReadAnythingAppModel::set_ukm_source_id(
+    const ukm::SourceId ukm_source_id) {
+  if (!base::Contains(tree_infos_, active_tree_id_)) {
+    return;
+  }
+  ReadAnythingAppModel::AXTreeInfo* tree_info =
+      tree_infos_.at(active_tree_id_).get();
+  if (!tree_info) {
+    return;
+  }
+  if (tree_info->ukm_source_id == ukm::kInvalidSourceId) {
+    tree_info->ukm_source_id = ukm_source_id;
+  } else {
+    DCHECK_EQ(tree_info->ukm_source_id, ukm_source_id);
+  }
+}
+
+int32_t ReadAnythingAppModel::num_selections() {
+  if (base::Contains(tree_infos_, active_tree_id_)) {
+    ReadAnythingAppModel::AXTreeInfo* tree_info =
+        tree_infos_.at(active_tree_id_).get();
+    if (tree_info) {
+      return tree_info->num_selections;
+    }
+  }
+  return 0;
+}
+
+void ReadAnythingAppModel::set_num_selections(
+    const int32_t& num_selections) {
+  if (!base::Contains(tree_infos_, active_tree_id_)) {
+    return;
+  }
+  ReadAnythingAppModel::AXTreeInfo* tree_info =
+      tree_infos_.at(active_tree_id_).get();
+  if (!tree_info) {
+    return;
+  }
+  tree_info->num_selections = num_selections;
 }
 
 ui::AXNode* ReadAnythingAppModel::GetAXNode(
diff --git a/chrome/renderer/accessibility/read_anything_app_model.h b/chrome/renderer/accessibility/read_anything_app_model.h
index 381d5a0..d48a37a 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.h
+++ b/chrome/renderer/accessibility/read_anything_app_model.h
@@ -26,10 +26,6 @@
 class AXSerializableTree;
 }  // namespace ui
 
-namespace ukm {
-class MojoUkmRecorder;
-}
-
 // A class that holds state for the ReadAnythingAppController for the Read
 // Anything WebUI app.
 class ReadAnythingAppModel {
@@ -49,17 +45,27 @@
     // AXTreeManagers.
     std::unique_ptr<ui::AXTreeManager> manager;
 
+    // The UKM source ID of the main frame that sources this AXTree. This is
+    // used for metrics collection. Only root AXTrees have this set.
+    ukm::SourceId ukm_source_id = ukm::kInvalidSourceId;
+
+    // Used to keep track of how many selections were made for the
+    // ukm_source_id. Only recorded during the select-to-distill flow (when the
+    // empty state page is shown).
+    int32_t num_selections = 0;
+
     // Whether URL information, namely is_docs, has been set.
     bool is_url_information_set = false;
 
     // Google Docs are different from regular webpages. We want to distill
-    // content from the annotated canvas elements, not the main tree.
+    // content from the annotated canvas elements, not the main tree. Only root
+    // AXTrees have this set.
     bool is_docs = false;
 
     // TODO(41496290): Include any information that is associated with a
-    // particular AXTree, namely is_pdf and ukm_id. Right now, those are set
-    // every time the active ax tree id changes; instead, they should be set
-    // once when a new tree is added.
+    // particular AXTree, namely is_pdf. Right now, this is set every time the
+    // active ax tree id changes; instead, it should be set once when a new tree
+    // is added.
   };
 
   // A current segment of text that will be consumed by Read Aloud.
@@ -171,10 +177,6 @@
     page_finished_loading_ = value;
   }
 
-  const ukm::SourceId& active_ukm_source_id() const {
-    return active_ukm_source_id_;
-  }
-
   const std::vector<ui::AXNodeID>& content_node_ids() const {
     return content_node_ids_;
   }
@@ -193,7 +195,12 @@
   void SetDistillationInProgress(bool distillation) {
     distillation_in_progress_ = distillation;
   }
-  void SetActiveUkmSourceId(const ukm::SourceId& source_id);
+
+  const ukm::SourceId& ukm_source_id();
+  void set_ukm_source_id(const ukm::SourceId ukm_source_id);
+  int32_t num_selections();
+  void set_num_selections(const int32_t& num_selections);
+
   void AddUrlInformationForTreeId(const ui::AXTreeID& tree_id);
   bool IsDocs() const;
 
@@ -420,10 +427,6 @@
   // child).
   ui::AXTreeID active_tree_id_ = ui::AXTreeIDUnknown();
 
-  // The UKM source ID of the main frame of the active web contents, whose
-  // AXTree has ID active_tree_id_. This is used for metrics collection.
-  ukm::SourceId active_ukm_source_id_ = ukm::kInvalidSourceId;
-
   // PDFs are handled differently than regular webpages. That is because they
   // are stored in a different web contents and the actual PDF text is inside an
   // iframe. In order to get tree information from the PDF web contents, we need
@@ -489,13 +492,6 @@
   ui::AXNodeID image_to_update_node_id_ = ui::kInvalidAXNodeID;
   bool selection_from_action_ = false;
 
-  std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder_;
-
-  // Used to keep track of how many selections were made for the
-  // active_ukm_source_id_. Only recorded during the select-to-distill flow
-  // (when the empty state page is shown).
-  int32_t num_selections_ = 0;
-
   // For screen2x data collection, Chrome is launched from the CLI to open one
   // webpage. We record the result of the distill() call for this entire
   // webpage, so we only make the call once the webpage finished loading.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7564c971..958c811 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3509,7 +3509,10 @@
         "../browser/lifetime/application_lifetime_browsertest.cc",
         "../browser/ui/views/enable_link_capturing_infobar_browsertest.cc",
       ]
-      deps += [ "//chrome/browser/apps/link_capturing" ]
+      deps += [
+        "//chrome/browser/apps/link_capturing",
+        "//chrome/browser/shortcuts:browser_tests",
+      ]
     }
 
     if (is_win || is_chromeos || is_mac) {
@@ -5586,6 +5589,7 @@
       "../browser/accessibility/live_caption/live_caption_surface_browsertest.cc",
       "../browser/chromeos/enterprise/cloud_storage/one_drive_pref_observer_browsertest.cc",
       "../browser/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager_lacros_browsertest.cc",
+      "../browser/chromeos/extensions/info_private_lacros_apitest.cc",
       "../browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_apitest.cc",
       "../browser/chromeos/extensions/odfs_config_private/odfs_config_private_api_browsertest.cc",
       "../browser/chromeos/extensions/wallpaper_apitest.cc",
diff --git a/chrome/test/base/ash/extension_js_browser_test.cc b/chrome/test/base/ash/extension_js_browser_test.cc
index a84d111..0b6d33a 100644
--- a/chrome/test/base/ash/extension_js_browser_test.cc
+++ b/chrome/test/base/ash/extension_js_browser_test.cc
@@ -13,21 +13,95 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/test/base/ash/javascript_browser_test.h"
+#include "chrome/test/base/test_switches.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/background_script_executor.h"
 #include "extensions/browser/browsertest_util.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_host_test_helper.h"
-#include "chrome/test/base/test_switches.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_observer.h"
+#include "extensions/browser/process_manager.h"
+#include "extensions/browser/service_worker/service_worker_host.h"
+#include "extensions/browser/service_worker/service_worker_task_queue.h"
+#include "extensions/common/extension.h"
 #include "ui/base/ime/ash/extension_ime_util.h"
 
 namespace {
 
+// Class to observe service worker readiness for the execution of test JS.
+class ExtensionTestObserver
+    : public extensions::ServiceWorkerTaskQueue::TestObserver,
+      public extensions::ExtensionRegistryObserver {
+ public:
+  explicit ExtensionTestObserver(const char* extension_id,
+                                 content::BrowserContext* context)
+      : extension_id_(extension_id), context_(context) {
+    extensions::ExtensionRegistry::Get(context_)->AddObserver(this);
+    extensions::ServiceWorkerTaskQueue::SetObserverForTest(this);
+  }
+
+  ~ExtensionTestObserver() override {
+    extensions::ExtensionRegistry::Get(context_)->RemoveObserver(this);
+    extensions::ServiceWorkerTaskQueue::SetObserverForTest(nullptr);
+  }
+
+  int WaitForManifestVersion() {
+    if (manifest_version_) {
+      return manifest_version_;
+    }
+    base::RunLoop waiter;
+    manifest_quit_ = waiter.QuitClosure();
+    waiter.Run();
+    return manifest_version_;
+  }
+
+  void WaitForServiceWorkerStart() {
+    if (started_) {
+      return;
+    }
+    base::RunLoop waiter;
+    started_quit_ = waiter.QuitClosure();
+    waiter.Run();
+  }
+
+  // extensions::ExtensionRegistryObserver:
+  void OnExtensionLoaded(content::BrowserContext* context,
+                         const extensions::Extension* extension) override {
+    if (context == context_ && extension->id() == extension_id_) {
+      manifest_version_ = extension->manifest_version();
+      if (manifest_quit_) {
+        std::move(manifest_quit_).Run();
+      }
+    }
+  }
+
+  // extensions::ServiceWorkerTaskQueue::TestObserver:
+  void DidStartWorker(const std::string& extension_id) override {
+    if (extension_id == extension_id_) {
+      started_ = true;
+      if (started_quit_) {
+        std::move(started_quit_).Run();
+      }
+    }
+  }
+
+ private:
+  const std::string extension_id_;
+  // Not owned.
+  raw_ptr<content::BrowserContext> context_;
+  size_t manifest_version_ = 0;
+  bool started_ = false;
+  base::OnceClosure manifest_quit_;
+  base::OnceClosure started_quit_;
+};
+
 const std::vector<std::string>& GetExtensionIdsToCollectCoverage() {
   static const std::vector<std::string> extensions_for_coverage = {
       extension_misc::kChromeVoxExtensionId,
@@ -73,8 +147,20 @@
 void ExtensionJSBrowserTest::WaitForExtension(const char* extension_id,
                                               base::OnceClosure load_cb) {
   extension_id_ = extension_id;
+
+  // ExtensionHosts only exist when there is an associated RenderFrame.
+  // Initialize both an ExtensionHostTestHelper and a ServiceWorkerObserver
+  // before running the load callback, to avoid missing the relevant event.
   extensions::ExtensionHostTestHelper host_helper(GetProfile(), extension_id);
+  ExtensionTestObserver observer(extension_id, GetProfile());
   std::move(load_cb).Run();
+
+  if (observer.WaitForManifestVersion() == 3) {
+    observer.WaitForServiceWorkerStart();
+    extension_host_browser_context_ = GetProfile();
+    return;
+  }
+
   extensions::ExtensionHost* extension_host =
       host_helper.WaitForHostCompletedFirstLoad();
   ASSERT_TRUE(extension_host);
diff --git a/chrome/test/data/extensions/api_test/chromeos_info_private/extended/background.js b/chrome/test/data/extensions/api_test/chromeos_info_private/extended/background.js
index ab1228f6..9b446fcb 100644
--- a/chrome/test/data/extensions/api_test/chromeos_info_private/extended/background.js
+++ b/chrome/test/data/extensions/api_test/chromeos_info_private/extended/background.js
@@ -18,6 +18,8 @@
       'assistantStatus',
       'isMeetDevice',
       'deviceRequisition',
+      'hwid',
+      'customizationId',
     ], chrome.test.callbackPass(function(values) {
           switch (testName) {
             case 'kiosk':
@@ -68,11 +70,20 @@
             case 'Is Meet Device - False' :
               chrome.test.assertFalse(values['isMeetDevice']);
               break;
-            case 'Device Requisition - Remora' :
+            case 'Machine Statistics Properties - Unset' :
+              chrome.test.assertEq('', values['deviceRequisition']);
+              chrome.test.assertEq('', values['hwid']);
+              chrome.test.assertEq('', values['customizationId']);
+              break;
+            case 'Device Requisition - Remora':
               chrome.test.assertEq('remora', values['deviceRequisition']);
               break;
-            case 'Device Requisition - Unset' :
-              chrome.test.assertEq('', values['deviceRequisition']);
+            case 'HWID':
+              chrome.test.assertEq('test_hw', values['hwid']);
+              break;
+            case 'CustomizationId':
+              chrome.test.assertEq('test_customization_id',
+                                   values['customizationId']);
               break;
           }
         }));
diff --git a/chrome/test/data/extensions/api_test/user_scripts/configure_world/worker.js b/chrome/test/data/extensions/api_test/user_scripts/configure_world/worker.js
index 75c42b2..9fd001a 100644
--- a/chrome/test/data/extensions/api_test/user_scripts/configure_world/worker.js
+++ b/chrome/test/data/extensions/api_test/user_scripts/configure_world/worker.js
@@ -50,6 +50,19 @@
 }
 
 chrome.test.runTests([
+  async function UserScriptWorld_worldIdValidation() {
+    await chrome.test.assertPromiseRejects(
+        chrome.userScripts.configureWorld({csp: '', worldId: ''}),
+        'Error: If specified, `worldId` must be non-empty.');
+    await chrome.test.assertPromiseRejects(
+        chrome.userScripts.configureWorld({csp: '', worldId: '_foobar'}),
+        `Error: World IDs beginning with '_' are reserved.`);
+    await chrome.test.assertPromiseRejects(
+        chrome.userScripts.configureWorld({csp: '', worldId: 'a'.repeat(257)}),
+        'Error: World IDs must be at most 256 characters.');
+    chrome.test.succeed();
+  },
+
   // Tests that a registered user script in the USER_SCRIPT world cannot send or
   // receive messages when messaging is disabled.
   async function UserScriptWorld_messagingDisabled() {
diff --git a/chrome/test/data/extensions/api_test/user_scripts/register/worker.js b/chrome/test/data/extensions/api_test/user_scripts/register/worker.js
index e289274..bcb04a1 100644
--- a/chrome/test/data/extensions/api_test/user_scripts/register/worker.js
+++ b/chrome/test/data/extensions/api_test/user_scripts/register/worker.js
@@ -223,6 +223,22 @@
     chrome.test.succeed();
   },
 
+  async function registeringScriptWithInvalidWorldIdThrowsAnError() {
+    await chrome.userScripts.unregister();
+
+    const scripts = [{
+      id: 'invalidMatchPattern',
+      matches: ['http://example.com/*'],
+      js: [{file: 'script.js'}],
+      worldId: '_'
+    }];
+
+    await chrome.test.assertPromiseRejects(
+        chrome.userScripts.register(scripts),
+        `Error: World IDs beginning with '_' are reserved.`);
+    chrome.test.succeed();
+  },
+
   // Tests that a registered user script with files is injected into a frame
   // where the extension has host permissions for and matches the script match
   // patterns.
diff --git a/chrome/test/data/extensions/api_test/user_scripts/update/worker.js b/chrome/test/data/extensions/api_test/user_scripts/update/worker.js
index 751714c..629e5e4 100644
--- a/chrome/test/data/extensions/api_test/user_scripts/update/worker.js
+++ b/chrome/test/data/extensions/api_test/user_scripts/update/worker.js
@@ -171,6 +171,32 @@
     chrome.test.succeed();
   },
 
+  async function updatingToAnInvalidWorldIdThrowsError() {
+    await chrome.userScripts.unregister();
+
+    // Register user script.
+    const scriptToRegister = [
+      {id: 'us1', matches: ['*://*/*'], js: [{file: 'user_script.js'}]},
+    ];
+    await chrome.userScripts.register(scriptToRegister);
+
+    // Updating a script with an invalid world ID should fail.
+    const scriptUpdate = [
+      {
+        id: 'us1',
+        matches: ['*://*/*'],
+        js: [{file: 'user_script.js'}],
+        worldId: '_',
+      }
+    ];
+
+    await chrome.test.assertPromiseRejects(
+        chrome.userScripts.update(scriptUpdate),
+        `Error: World IDs beginning with '_' are reserved.`);
+
+    chrome.test.succeed();
+  },
+
   // Tests that calling userScripts.update with a specific ID updates such
   // script and does not inject them into a (former) matching frame.
   async function scriptUpdated() {
diff --git a/chrome/test/data/focus_rings/focus_ring_browsertest_anchor_win.png b/chrome/test/data/focus_rings/focus_ring_browsertest_anchor_win.png
index 6b146c1..b50c35e 100644
--- a/chrome/test/data/focus_rings/focus_ring_browsertest_anchor_win.png
+++ b/chrome/test/data/focus_rings/focus_ring_browsertest_anchor_win.png
Binary files differ
diff --git a/chrome/test/data/focus_rings/focus_ring_browsertest_button_win.png b/chrome/test/data/focus_rings/focus_ring_browsertest_button_win.png
index 3fced26..0680afc6 100644
--- a/chrome/test/data/focus_rings/focus_ring_browsertest_button_win.png
+++ b/chrome/test/data/focus_rings/focus_ring_browsertest_button_win.png
Binary files differ
diff --git a/chrome/test/data/focus_rings/focus_ring_browsertest_checkbox_win.png b/chrome/test/data/focus_rings/focus_ring_browsertest_checkbox_win.png
index e13284c..5eb51cb 100644
--- a/chrome/test/data/focus_rings/focus_ring_browsertest_checkbox_win.png
+++ b/chrome/test/data/focus_rings/focus_ring_browsertest_checkbox_win.png
Binary files differ
diff --git a/chrome/test/data/focus_rings/focus_ring_browsertest_dark_mode_button_win.png b/chrome/test/data/focus_rings/focus_ring_browsertest_dark_mode_button_win.png
index 89d7308..a621f12e 100644
--- a/chrome/test/data/focus_rings/focus_ring_browsertest_dark_mode_button_win.png
+++ b/chrome/test/data/focus_rings/focus_ring_browsertest_dark_mode_button_win.png
Binary files differ
diff --git a/chrome/test/data/focus_rings/focus_ring_browsertest_radio_win.png b/chrome/test/data/focus_rings/focus_ring_browsertest_radio_win.png
index 62241ce..0bce232 100644
--- a/chrome/test/data/focus_rings/focus_ring_browsertest_radio_win.png
+++ b/chrome/test/data/focus_rings/focus_ring_browsertest_radio_win.png
Binary files differ
diff --git a/chrome/test/data/shortcuts/default_icon_has_two/16_favicon_part.png b/chrome/test/data/shortcuts/default_icon_has_two/16_favicon_part.png
new file mode 100644
index 0000000..4d217e8
--- /dev/null
+++ b/chrome/test/data/shortcuts/default_icon_has_two/16_favicon_part.png
Binary files differ
diff --git a/chrome/test/data/shortcuts/default_icon_has_two/32_favicon_part.png b/chrome/test/data/shortcuts/default_icon_has_two/32_favicon_part.png
new file mode 100644
index 0000000..81e1fa4
--- /dev/null
+++ b/chrome/test/data/shortcuts/default_icon_has_two/32_favicon_part.png
Binary files differ
diff --git a/chrome/test/data/shortcuts/default_icon_has_two/favicon.ico b/chrome/test/data/shortcuts/default_icon_has_two/favicon.ico
new file mode 100644
index 0000000..e27eec3
--- /dev/null
+++ b/chrome/test/data/shortcuts/default_icon_has_two/favicon.ico
Binary files differ
diff --git a/chrome/test/data/shortcuts/default_icon_has_two/index.html b/chrome/test/data/shortcuts/default_icon_has_two/index.html
new file mode 100644
index 0000000..94d7fa4
--- /dev/null
+++ b/chrome/test/data/shortcuts/default_icon_has_two/index.html
@@ -0,0 +1,11 @@
+<html>
+
+<title>Page without default favicon</title>
+
+<head></head>
+
+<body>
+  <h1>This page has a default favicon, with two icons inside it.</h1>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/shortcuts/green.png b/chrome/test/data/shortcuts/green.png
new file mode 100644
index 0000000..c7510d38
--- /dev/null
+++ b/chrome/test/data/shortcuts/green.png
Binary files differ
diff --git a/chrome/test/data/shortcuts/no_icons_page.html b/chrome/test/data/shortcuts/no_icons_page.html
new file mode 100644
index 0000000..42d323d
--- /dev/null
+++ b/chrome/test/data/shortcuts/no_icons_page.html
@@ -0,0 +1,10 @@
+<html>
+<title>Page without icons</title>
+
+<head></head>
+
+<body>
+  <h1>This page has no icons.</h1>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/shortcuts/noise.png b/chrome/test/data/shortcuts/noise.png
new file mode 100644
index 0000000..81acb31
--- /dev/null
+++ b/chrome/test/data/shortcuts/noise.png
Binary files differ
diff --git a/chrome/test/data/shortcuts/page_icons.html b/chrome/test/data/shortcuts/page_icons.html
new file mode 100644
index 0000000..40f787d91
--- /dev/null
+++ b/chrome/test/data/shortcuts/page_icons.html
@@ -0,0 +1,13 @@
+<html>
+<title>Page with icon links</title>
+
+<head>
+  <link rel="apple-touch-icon" href="green.png" />
+  <link rel="icon" href="noise.png" />
+</head>
+
+<body>
+  <h1>Page with icon links</h1>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/webui/commerce/product_specifications/BUILD.gn b/chrome/test/data/webui/commerce/product_specifications/BUILD.gn
index 268ab75..975aa4a 100644
--- a/chrome/test/data/webui/commerce/product_specifications/BUILD.gn
+++ b/chrome/test/data/webui/commerce/product_specifications/BUILD.gn
@@ -7,14 +7,16 @@
 build_webui_tests("build") {
   files = [
     "app_test.ts",
+    "product_selector_test.ts",
     "table_test.ts",
   ]
-  ts_path_mappings =
-      [ "chrome://compare/*|" +
-        rebase_path("$root_gen_dir/chrome/browser/resources/commerce/product_specifications/tsc/*",
-                    target_gen_dir) ]
+  ts_path_mappings = [ "chrome://compare/*|" + rebase_path(
+                           "$root_gen_dir/chrome/browser/resources/commerce/product_specifications/tsc/*",
+                           target_gen_dir) ]
   ts_deps = [
     "//chrome/browser/resources/commerce/product_specifications:build_ts",
     "//ui/webui/resources/cr_components/commerce:build_ts",
+    "//ui/webui/resources/js:build_ts",
+    "//ui/webui/resources/mojo:build_ts",
   ]
 }
diff --git a/chrome/test/data/webui/commerce/product_specifications/product_selector_test.ts b/chrome/test/data/webui/commerce/product_specifications/product_selector_test.ts
new file mode 100644
index 0000000..445742ac
--- /dev/null
+++ b/chrome/test/data/webui/commerce/product_specifications/product_selector_test.ts
@@ -0,0 +1,55 @@
+// 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://compare/product_selector.js';
+
+import type {ProductSelectorElement} from 'chrome://compare/product_selector.js';
+import {BrowserProxyImpl} from 'chrome://resources/cr_components/commerce/browser_proxy.js';
+import {stringToMojoString16, stringToMojoUrl} from 'chrome://resources/js/mojo_type_util.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {TestMock} from 'chrome://webui-test/test_mock.js';
+
+suite('ProductSelectorTest', () => {
+  const shoppingServiceApi = TestMock.fromClass(BrowserProxyImpl);
+
+  async function createSelector(): Promise<ProductSelectorElement> {
+    const selector = document.createElement('product-selector');
+    document.body.appendChild(selector);
+    await flushTasks();
+    return selector;
+  }
+
+  setup(async () => {
+    shoppingServiceApi.reset();
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    BrowserProxyImpl.setInstance(shoppingServiceApi);
+  });
+
+  test('OpenTabsShown', async () => {
+    const titleString = 'title';
+    const openTabs = [{
+      title: stringToMojoString16(titleString),
+      url: stringToMojoUrl('http://example.com'),
+    }];
+    const selector = await createSelector();
+
+    shoppingServiceApi.setResultFor(
+        'getUrlInfosForOpenTabs', Promise.resolve({urlInfos: openTabs}));
+
+    assertEquals(0, shoppingServiceApi.getCallCount('getUrlInfosForOpenTabs'));
+
+    selector.$.currentItemButton.click();
+
+    await shoppingServiceApi.whenCalled('getUrlInfosForOpenTabs');
+
+    await flushTasks();
+
+    // Ensure the number of list items is equal to the number of open tabs.
+    assertEquals(openTabs.length, selector.openTabs.length);
+
+    assertEquals(titleString, selector.openTabs[0]!.title);
+    assertEquals(openTabs[0]!.url.url, selector.openTabs[0]!.url);
+  });
+});
diff --git a/chrome/test/data/webui/commerce/product_specifications/product_specifications_browsertest.cc b/chrome/test/data/webui/commerce/product_specifications/product_specifications_browsertest.cc
index e8f1b615..09b3791 100644
--- a/chrome/test/data/webui/commerce/product_specifications/product_specifications_browsertest.cc
+++ b/chrome/test/data/webui/commerce/product_specifications/product_specifications_browsertest.cc
@@ -28,3 +28,8 @@
 IN_PROC_BROWSER_TEST_F(ProductSpecificationsTest, Table) {
   RunTest("commerce/product_specifications/table_test.js", "mocha.run()");
 }
+
+IN_PROC_BROWSER_TEST_F(ProductSpecificationsTest, ProductSelector) {
+  RunTest("commerce/product_specifications/product_selector_test.js",
+          "mocha.run()");
+}
diff --git a/chrome/test/data/webui/commerce/product_specifications/table_test.ts b/chrome/test/data/webui/commerce/product_specifications/table_test.ts
index 3f24ec6..48eb41b0 100644
--- a/chrome/test/data/webui/commerce/product_specifications/table_test.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/table_test.ts
@@ -17,21 +17,17 @@
     document.body.appendChild(tableElement);
   });
 
-  test('product columns show the correct data', async () => {
-    // Arrange.
-    const title1 = 'foo';
-    const title2 = 'bar';
-
-    // Act.
-    tableElement.columns = [{title: title1}, {title: title2}];
+  test('column count correct', async () => {
+    // Arrange / Act.
+    tableElement.columns = [
+      {selectedItem: {title: 'title', url: '', imageUrl: ''}},
+      {selectedItem: {title: 'title2', url: '', imageUrl: ''}},
+    ];
     await waitAfterNextRender(tableElement);
 
     // Assert.
-    const columnTitles =
-        tableElement.shadowRoot!.querySelectorAll('.col .col-card');
-    assertEquals(2, columnTitles.length);
-    assertEquals(title1, columnTitles[0]!.textContent);
-    assertEquals(title2, columnTitles[1]!.textContent);
+    const columns = tableElement.shadowRoot!.querySelectorAll('.col');
+    assertEquals(2, columns.length);
   });
 
   test('product rows show the correct data', async () => {
diff --git a/chrome/test/data/webui/cr_elements/cr_icon_test.ts b/chrome/test/data/webui/cr_elements/cr_icon_test.ts
index 6c736049..6aa6a27 100644
--- a/chrome/test/data/webui/cr_elements/cr_icon_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_icon_test.ts
@@ -126,4 +126,39 @@
     svg = icon.shadowRoot!.querySelector('svg');
     assertTrue(!!svg);
   });
+
+  test('cr-iconset used rather than iron-iconset', async () => {
+    // Add an iron-iconset to the document.
+    const template = html`<iron-iconset-svg name="cr20-test" size="20">
+      <svg>
+        <defs>
+          <g id="arrow">
+            <path d="M7 10l5 5 5-5z"></path>
+          </g>
+        </defs>
+      </svg>
+    </iron-iconset-svg>`;
+    document.head.appendChild(template.content);
+
+    // Add a cr-iconset with the same name.
+    const iconsetHtml = litHtml`
+      <cr-iconset name="cr20-test" size="20">
+        <svg>
+          <defs>
+            <g id="arrow">
+              <path d="M7 14l5-5 5 5z"></path>
+            </g>
+          </defs>
+        </svg>
+      </cr-iconset>`;
+    render(iconsetHtml, document.head);
+
+    icon.icon = 'cr20-test:arrow';
+    await microtasksFinished();
+    const svg = icon.shadowRoot!.querySelector('svg');
+    assertTrue(!!svg);
+
+    // Confirm the cr-iconset value.
+    assertSvgPath(svg, 'M7 14l5-5 5 5z');
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index bab12d1..72cf7bb 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -350,6 +350,7 @@
     "os_settings_ui/os_settings_ui_menu_test.ts",
     "os_settings_ui/os_settings_ui_page_availability_test.ts",
     "os_settings_ui/os_settings_ui_page_visibility_revamp_test.ts",
+    "os_settings_ui/os_settings_ui_pref_sync_test.ts",
     "os_settings_ui/os_settings_ui_test.ts",
     "os_settings_ui/os_settings_ui_toolbar_test.ts",
     "os_settings_ui/page_availability_test_helpers.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/internet_page/internet_detail_subpage_test.ts b/chrome/test/data/webui/settings/chromeos/internet_page/internet_detail_subpage_test.ts
index 0c6a9df..e925626 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_page/internet_detail_subpage_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/internet_page/internet_detail_subpage_test.ts
@@ -1963,6 +1963,79 @@
       });
     });
 
+    [true, false].forEach(isApnPoliciesEnabled => {
+      test(
+          `Managed APN icon visibility when isApnPoliciesEnabled is ${
+              isApnPoliciesEnabled}`,
+          async () => {
+            loadTimeData.overrideValues({
+              isApnRevampEnabled: true,
+              isApnPoliciesEnabled: isApnPoliciesEnabled,
+            });
+            init();
+            mojoApi.setNetworkTypeEnabledState(NetworkType.kCellular, true);
+            const apnName = 'test';
+            const testIccid = '11111';
+            const cellularNetwork =
+                getManagedProperties(NetworkType.kCellular, 'cellular');
+            cellularNetwork.typeProperties.cellular!.connectedApn = {
+              accessPointName: '',
+              id: '',
+              authentication: ApnAuthenticationType.kAutomatic,
+              language: undefined,
+              localizedName: undefined,
+              name: undefined,
+              password: undefined,
+              username: undefined,
+              attach: undefined,
+              state: ApnState.kEnabled,
+              ipType: ApnIpType.kAutomatic,
+              apnTypes: [],
+              source: ApnSource.kModb,
+            };
+            cellularNetwork.typeProperties.cellular!.connectedApn!
+                .accessPointName = apnName;
+            cellularNetwork.typeProperties.cellular!.iccid = testIccid;
+            mojoApi.setManagedPropertiesForTest(cellularNetwork);
+            internetDetailPage.init('cellular_guid', 'Cellular', 'cellular');
+
+            // Set cellular network as active SIM so that APN row should show up
+            // if the flag is enabled.
+            mojoApi.setDeviceStateForTest({
+              ...getDefaultDeviceStateProps(),
+              deviceState: DeviceStateType.kEnabled,
+              simInfos: [{
+                iccid: testIccid,
+                isPrimary: true,
+                slotId: 0,
+                eid: '',
+              }],
+            });
+            await flushTasks();
+            assertTrue(!!internetDetailPage.shadowRoot!.querySelector(
+                '#apnSubpageButton'));
+
+            // Check for APN policies managed icon.
+            const getApnManagedIcon = () =>
+                internetDetailPage.shadowRoot!.querySelector('#apnManagedIcon');
+            assertFalse(!!getApnManagedIcon());
+
+            internetDetailPage.globalPolicy = {
+              ...getDefaultGlobalPolicy(),
+              allowApnModification: true,
+            };
+            await flushTasks();
+            assertFalse(!!getApnManagedIcon());
+
+            internetDetailPage.globalPolicy = {
+              ...getDefaultGlobalPolicy(),
+              allowApnModification: false,
+            };
+            await flushTasks();
+            assertEquals(isApnPoliciesEnabled, !!getApnManagedIcon());
+          });
+    });
+
     test('Cellular network not found while in detail subpage', async () => {
       init();
       mojoApi.setNetworkTypeEnabledState(NetworkType.kCellular, true);
diff --git a/chrome/test/data/webui/settings/chromeos/os_apps_page/os_apps_page_test.ts b/chrome/test/data/webui/settings/chromeos/os_apps_page/os_apps_page_test.ts
index 413da33..be504cc 100644
--- a/chrome/test/data/webui/settings/chromeos/os_apps_page/os_apps_page_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_apps_page/os_apps_page_test.ts
@@ -25,6 +25,8 @@
 
 const isRevampWayfindingEnabled =
     loadTimeData.getBoolean('isRevampWayfindingEnabled');
+const isAppParentalControlsAvailable =
+    loadTimeData.getBoolean('isAppParentalControlsFeatureAvailable');
 let appsPage: OsSettingsAppsPageElement;
 let androidAppsBrowserProxy: TestAndroidAppsBrowserProxy;
 
@@ -49,6 +51,13 @@
         value: 2,
       },
     },
+    on_device_app_controls: {
+      setup_completed: {
+        key: 'on_device_app_controls.setup_completed',
+        type: chrome.settingsPrivate.PrefType.BOOLEAN,
+        value: false,
+      },
+    },
   };
 }
 
@@ -376,33 +385,74 @@
       assertTrue(isVisible(rowLink));
     });
 
-    test(
-      'Clicking app parental controls row opens subpage',
+    if (isAppParentalControlsAvailable) {
+      test(
+        'Clicking set up sets up parental controls and navigates to subpage',
         () => {
-          const rowLink = appsPage.shadowRoot!.querySelector<CrLinkRowElement>(
-              '#appParentalControlsRow');
-          // Row link is visible when the parental controls feature is enabled.
-          if (loadTimeData.getBoolean(
-                  'isAppParentalControlsFeatureAvailable')) {
-            assertTrue(!!rowLink);
-            assertTrue(isVisible(rowLink));
+          const parentalControlsRow =
+            appsPage.shadowRoot!.querySelector<HTMLElement>(
+              '#appParentalControls');
+            assertTrue(!!parentalControlsRow);
+            assertTrue(isVisible(parentalControlsRow));
+
+            const setUpButton =
+                parentalControlsRow.querySelector<HTMLElement>('cr-button');
+            assertTrue(!!setUpButton);
+            setUpButton.click();
+            flush();
 
             assertNull(appsPage.shadowRoot!.querySelector(
                 'settings-app-parental-controls-subpage'));
-            rowLink.click();
+            const subpageArrow = parentalControlsRow.querySelector<HTMLElement>(
+                '.subpage-arrow');
+            assertTrue(!!subpageArrow);
+            subpageArrow.click();
+            flush();
+
             assertTrue(!!appsPage.shadowRoot!.querySelector(
-                'settings-app-parental-controls-subpage'));
-          } else {
-            assertNull(rowLink);
-          }
+              'settings-app-parental-controls-subpage'));
         });
 
+      test('Toggling parental controls resets parental controls', () => {
+        const parentalControlsRow =
+          appsPage.shadowRoot!.querySelector<HTMLElement>(
+            '#appParentalControls');
+        assertTrue(!!parentalControlsRow);
+        assertTrue(isVisible(parentalControlsRow));
+
+        const setUpButton =
+            parentalControlsRow.querySelector<HTMLElement>('cr-button');
+        assertTrue(!!setUpButton);
+        setUpButton.click();
+        flush();
+
+        const toggle =
+            parentalControlsRow.querySelector<HTMLElement>('cr-toggle');
+        assertTrue(!!toggle);
+        toggle.click();
+        flush();
+
+        assertTrue(
+            !!parentalControlsRow.querySelector<HTMLElement>('cr-button'));
+      });
+    }
+
+    if (!isAppParentalControlsAvailable) {
+      test('Parental controls row not visible when feature off', () => {
+        const parentalControlsRow =
+          appsPage.shadowRoot!.querySelector<HTMLElement>(
+            '#appParentalControls');
+        assertNull(parentalControlsRow);
+      });
+    }
+
     test('Clicking enable button enables ARC', () => {
-      const button =
-          appsPage.shadowRoot!.querySelector<HTMLButtonElement>('#enable');
+      const androidApps = appsPage.shadowRoot!.querySelector('#androidApps');
+      assertTrue(!!androidApps);
+      const button = androidApps.querySelector<HTMLButtonElement>('#arcEnable');
       assertTrue(!!button);
       assertTrue(isVisible(button));
-      assertNull(appsPage.shadowRoot!.querySelector('.subpage-arrow'));
+      assertNull(androidApps.querySelector('.subpage-arrow'));
 
       button.click();
       flush();
@@ -413,8 +463,7 @@
         settingsAppAvailable: false,
       };
       flush();
-      assertTrue(
-          isVisible(appsPage.shadowRoot!.querySelector('.subpage-arrow')));
+      assertTrue(isVisible(androidApps.querySelector('.subpage-arrow')));
     });
 
     // On startup row does not exist in the apps page under the revamp.
@@ -490,7 +539,7 @@
       Router.getInstance().navigateTo(routes.APPS, params);
 
       const deepLinkElement =
-          appsPage.shadowRoot!.querySelector<HTMLButtonElement>('#enable');
+        appsPage.shadowRoot!.querySelector<HTMLButtonElement>('#arcEnable');
       assertTrue(!!deepLinkElement);
       assertTrue(isVisible(deepLinkElement));
       await waitAfterNextRender(deepLinkElement);
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.cc b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.cc
index f12b9a8..143f2a0 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.cc
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.cc
@@ -1780,6 +1780,11 @@
   RunSettingsTest("os_settings_ui/os_settings_ui_page_availability_test.js");
 }
 
+IN_PROC_BROWSER_TEST_P(OSSettingsRevampMochaTest,
+                       OsSettingsUiPrefSync) {
+  RunSettingsTest("os_settings_ui/os_settings_ui_pref_sync_test.js");
+}
+
 IN_PROC_BROWSER_TEST_F(OSSettingsMochaTestRevampEnabled,
                        OsSettingsUiPageVisibilityRevamp) {
   RunSettingsTest(
@@ -1886,6 +1891,58 @@
       "parental_controls_page/parental_controls_settings_card_test.js");
 }
 
+class OSSettingsRevampMochaTestFasterSplitScreenEnabled
+    : public OSSettingsRevampMochaTest {
+ public:
+  OSSettingsRevampMochaTestFasterSplitScreenEnabled() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled=*/{ash::features::kFasterSplitScreenSetup},
+        /*disabled=*/{});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    RevampParameterized,
+    OSSettingsRevampMochaTestFasterSplitScreenEnabled,
+    testing::Bool(),
+    OSSettingsRevampMochaTestFasterSplitScreenEnabled::DescribeParams);
+
+class OSSettingsRevampMochaTestFasterSplitScreenDisabled
+    : public OSSettingsRevampMochaTest {
+ public:
+  OSSettingsRevampMochaTestFasterSplitScreenDisabled() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled=*/{},
+        /*disabled=*/{ash::features::kFasterSplitScreenSetup});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    RevampParameterized,
+    OSSettingsRevampMochaTestFasterSplitScreenDisabled,
+    testing::Bool(),
+    OSSettingsRevampMochaTestFasterSplitScreenDisabled::DescribeParams);
+
+IN_PROC_BROWSER_TEST_P(OSSettingsRevampMochaTestFasterSplitScreenEnabled,
+                       PersonalizationPageWithPersonalizationHub) {
+  RunSettingsTest(
+      "personalization_page/"
+      "personalization_page_with_personalization_hub_test.js");
+}
+
+IN_PROC_BROWSER_TEST_P(OSSettingsRevampMochaTestFasterSplitScreenDisabled,
+                       PersonalizationPageWithPersonalizationHub) {
+  RunSettingsTest(
+      "personalization_page/"
+      "personalization_page_with_personalization_hub_test.js");
+}
+
 IN_PROC_BROWSER_TEST_F(OSSettingsMochaTest,
                        PersonalizationPageWithPersonalizationHub) {
   RunSettingsTest(
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_pref_sync_test.ts b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_pref_sync_test.ts
new file mode 100644
index 0000000..e9677d0
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_pref_sync_test.ts
@@ -0,0 +1,54 @@
+// 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.
+
+/**
+ * @fileoverview
+ * Suite of tests for the overall OS Settings UI asserting pref sync behavior
+ * via "user-action-setting-pref-change" event. Separated into a dedicated test
+ * suite to avoid timeouts since the <os-settings-ui> element is very large.
+ */
+
+import 'chrome://os-settings/os_settings.js';
+
+import {CrSettingsPrefs, OsSettingsUiElement, SettingsPrefsElement} from 'chrome://os-settings/os_settings.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+
+suite('<os-settings-ui> pref sync', () => {
+  let uiElement: OsSettingsUiElement;
+  let settingsPrefs: SettingsPrefsElement;
+
+  suiteSetup(async () => {
+    uiElement = document.createElement('os-settings-ui');
+    document.body.appendChild(uiElement);
+    await flushTasks();
+    await CrSettingsPrefs.initialized;
+
+    const queriedElement =
+        uiElement.shadowRoot!.querySelector('settings-prefs');
+    assertTrue(!!queriedElement);
+    settingsPrefs = queriedElement;
+  });
+
+  test('Pref is updated via "user-action-setting-pref-change" event', () => {
+    const prefKey = 'ash.app_notification_badging_enabled';
+
+    // Pref value is default true.
+    const defaultValue = true;
+    let prefObject = settingsPrefs.get(`prefs.${prefKey}`);
+    assertTrue(!!prefObject);
+    assertEquals(defaultValue, prefObject.value);
+
+    uiElement.dispatchEvent(new CustomEvent('user-action-setting-pref-change', {
+      bubbles: true,
+      composed: true,
+      detail: {prefKey, value: !defaultValue},
+    }));
+
+    // Pref value is updated to false.
+    prefObject = settingsPrefs.get(`prefs.${prefKey}`);
+    assertTrue(!!prefObject);
+    assertEquals(!defaultValue, prefObject.value);
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/personalization_page/personalization_page_with_personalization_hub_test.ts b/chrome/test/data/webui/settings/chromeos/personalization_page/personalization_page_with_personalization_hub_test.ts
index 41700a3..3cd49b1d 100644
--- a/chrome/test/data/webui/settings/chromeos/personalization_page/personalization_page_with_personalization_hub_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/personalization_page/personalization_page_with_personalization_hub_test.ts
@@ -12,10 +12,11 @@
 
 import {TestPersonalizationHubBrowserProxy} from './test_personalization_hub_browser_proxy.js';
 
-let personalizationPage: SettingsPersonalizationPageElement;
-let personalizationHubBrowserProxy: TestPersonalizationHubBrowserProxy;
-
 suite('<settings-personalization-page>', () => {
+  let personalizationPage: SettingsPersonalizationPageElement;
+  let personalizationHubBrowserProxy: TestPersonalizationHubBrowserProxy;
+  const shouldShowMultitaskingInPersonalization = loadTimeData.getBoolean('shouldShowMultitaskingInPersonalization');
+
   async function createPersonalizationPage(): Promise<void> {
     personalizationPage =
         document.createElement('settings-personalization-page');
@@ -70,45 +71,41 @@
     await personalizationHubBrowserProxy.whenCalled('openPersonalizationHub');
   });
 
-  test(
-      'Multitasking settings subsection is visible with feature enabled',
-      async () => {
-        loadTimeData.overrideValues(
-            {shouldShowMultitaskingInPersonalization: true});
-        await createPersonalizationPage();
-        const multitaskingSettingsSubsection =
-            personalizationPage.shadowRoot!.querySelector<HTMLButtonElement>(
-                '#snapWindowSuggestionsSubsection');
-        assertTrue(
-            isVisible(multitaskingSettingsSubsection),
-            'Multitasking settings subsection should be visible.');
-      });
+  if (shouldShowMultitaskingInPersonalization) {
+    test(
+        'Multitasking settings subsection is visible with feature enabled',
+        async () => {
+          await createPersonalizationPage();
+          const multitaskingSettingsSubsection =
+              personalizationPage.shadowRoot!.querySelector<HTMLButtonElement>(
+                  '#snapWindowSuggestionsSubsection');
+          assertTrue(
+              isVisible(multitaskingSettingsSubsection),
+              'Multitasking settings subsection should be visible.');
+        });
 
-  test(
+    test('Multitasking settings subsection is deep-linkable', async () => {
+      await createPersonalizationPage();
+      await deepLinkToSetting(settingMojom.Setting.kSnapWindowSuggestions);
+
+      const multitaskingSettingsSubsection =
+        personalizationPage.shadowRoot!.querySelector<HTMLButtonElement>(
+          '#snapWindowSuggestionsSubsection');
+      assertTrue(!!multitaskingSettingsSubsection);
+      await assertElementIsDeepLinked(multitaskingSettingsSubsection);
+    });
+  } else {
+    test(
       'Multitasking settings subsection is not visible with feature disabled',
       async () => {
-        loadTimeData.overrideValues(
-            {shouldShowMultitaskingInPersonalization: false});
         await createPersonalizationPage();
 
         const multitaskingSettingsSubsection =
-            personalizationPage.shadowRoot!.querySelector<HTMLButtonElement>(
-                '#snapWindowSuggestionsSubsection');
-        assertFalse(
-            isVisible(multitaskingSettingsSubsection),
-            'Multitasking settings subsection should not be visible.');
-      });
-
-  test('Multitasking settings subsection is deep-linkable', async () => {
-    loadTimeData.overrideValues(
-        {shouldShowMultitaskingInPersonalization: true});
-    await createPersonalizationPage();
-    await deepLinkToSetting(settingMojom.Setting.kSnapWindowSuggestions);
-
-    const multitaskingSettingsSubsection =
-        personalizationPage.shadowRoot!.querySelector<HTMLButtonElement>(
+          personalizationPage.shadowRoot!.querySelector<HTMLButtonElement>(
             '#snapWindowSuggestionsSubsection');
-    assertTrue(!!multitaskingSettingsSubsection);
-    await assertElementIsDeepLinked(multitaskingSettingsSubsection);
-  });
+        assertFalse(
+          isVisible(multitaskingSettingsSubsection),
+          'Multitasking settings subsection should not be visible.');
+      });
+  }
 });
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
index c7488fd..9b9ac52 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
@@ -239,7 +239,7 @@
             categoriesElement,
             '#classicChromeTile #cornerNewTabPageTile #cornerNewTabPage')!.src,
         'chrome://customize-chrome-side-panel.top-chrome/icons/' +
-            'gm3_corner_new_tab_page.svg');
+            'corner_new_tab_page.svg');
   });
 
   [true, false].forEach((flagEnabled) => {
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/theme_snapshot_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/theme_snapshot_test.ts
index 5a884ac..0b6dd5e 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/theme_snapshot_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/theme_snapshot_test.ts
@@ -158,7 +158,7 @@
         $$<SVGUseElement>(
             themeSnapshotElement,
             '#classicChromeBackground svg use')!.href.baseVal,
-        'icons/gm3_mini_new_tab_page.svg#miniNewTabPage');
+        'icons/mini_new_tab_page.svg#miniNewTabPage');
   });
 
   test(
diff --git a/chrome/test/data/webui/side_panel/read_anything/voice_selection_menu_test.ts b/chrome/test/data/webui/side_panel/read_anything/voice_selection_menu_test.ts
index b85df2a..6a63196 100644
--- a/chrome/test/data/webui/side_panel/read_anything/voice_selection_menu_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/voice_selection_menu_test.ts
@@ -4,9 +4,10 @@
 
 import 'chrome-untrusted://read-anything-side-panel.top-chrome/voice_selection_menu.js';
 
+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 type {VoiceSelectionMenuElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/voice_selection_menu.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
+import {assertEquals, assertFalse, assertStringContains, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 
 function stringToHtmlTestId(s: string): string {
   return s.replace(/\s/g, '-').replace(/[()]/g, '');
@@ -271,17 +272,24 @@
       test('it shows preview-playing button when preview plays', () => {
         const playIconVoice0 =
             getDropdownItemForVoice(availableVoices[0]!)
-                .querySelector<HTMLButtonElement>('#play-icon')!;
+                .querySelector<CrIconButtonElement>('#preview-icon')!;
         const playIconOfPreviewVoice =
             getDropdownItemForVoice(previewVoice)
-                .querySelector<HTMLButtonElement>('#play-icon')!;
+                .querySelector<CrIconButtonElement>('#preview-icon')!;
 
-        // The play icon should flip to disabled for the voice being previewed
+        // The play icon should flip to pause for the voice being previewed
         assertTrue(isPositionedOnPage(playIconOfPreviewVoice));
-        assertTrue(isDisabled(playIconOfPreviewVoice));
-        // The play icon should remain enabled for the other buttons
+        assertEquals(
+            playIconOfPreviewVoice.ironIcon, 'read-anything-20:pause-circle');
+        assertStringContains(
+            playIconOfPreviewVoice.title.toLowerCase(), 'pause');
+        assertStringContains(
+            playIconOfPreviewVoice.ariaLabel!.toLowerCase(), 'pause');
+        // The play icon should remain unchanged for the other buttons
         assertTrue(isPositionedOnPage(playIconVoice0));
-        assertFalse(isDisabled(playIconVoice0));
+        assertEquals(playIconVoice0.ironIcon, 'read-anything-20:play-circle');
+        assertStringContains(playIconVoice0.title.toLowerCase(), 'play');
+        assertStringContains(playIconVoice0.ariaLabel!.toLowerCase(), 'play');
       });
 
       suite('when preview finishes playing', () => {
@@ -293,20 +301,27 @@
           flush();
         });
 
-        test('it flips the preview button back to enabled', () => {
+        test('it flips the preview button back to play icon', () => {
           const playIconVoice0 =
               getDropdownItemForVoice(availableVoices[0]!)
-                  .querySelector<HTMLButtonElement>('#play-icon')!;
+                  .querySelector<CrIconButtonElement>('#preview-icon')!;
           const playIconOfPreviewVoice =
               getDropdownItemForVoice(availableVoices[1]!)
-                  .querySelector<HTMLButtonElement>('#play-icon')!;
+                  .querySelector<CrIconButtonElement>('#preview-icon')!;
 
-          // All icons should be enabled play icons because no preview is
+          // All icons should be play icons because no preview is
           // playing
           assertTrue(isPositionedOnPage(playIconOfPreviewVoice));
           assertTrue(isPositionedOnPage(playIconVoice0));
-          assertFalse(isDisabled(playIconVoice0));
-          assertFalse(isDisabled(playIconOfPreviewVoice));
+          assertEquals(
+              playIconOfPreviewVoice.ironIcon, 'read-anything-20:play-circle');
+          assertEquals(playIconVoice0.ironIcon, 'read-anything-20:play-circle');
+          assertStringContains(
+              playIconOfPreviewVoice.title.toLowerCase(), 'play');
+          assertStringContains(playIconVoice0.title.toLowerCase(), 'play');
+          assertStringContains(
+              playIconOfPreviewVoice.ariaLabel!.toLowerCase(), 'play');
+          assertStringContains(playIconVoice0.ariaLabel!.toLowerCase(), 'play');
         });
       });
     });
@@ -322,7 +337,3 @@
       !!(element.offsetWidth || element.offsetHeight ||
          element.getClientRects().length);
 }
-
-function isDisabled(element: HTMLButtonElement) {
-  return element.disabled;
-}
diff --git a/chrome/updater/util/util_win_unittest.cc b/chrome/updater/util/util_win_unittest.cc
index 940e89f..0e885cd 100644
--- a/chrome/updater/util/util_win_unittest.cc
+++ b/chrome/updater/util/util_win_unittest.cc
@@ -56,4 +56,19 @@
   EXPECT_TRUE(cmd_line->GetSwitchValueASCII("s3").empty());
 }
 
+TEST(UtilTest, CommandLineForLegacyFormat_SwitchWithEqualSign) {
+  std::optional<base::CommandLine> cmd_line = CommandLineForLegacyFormat(
+      L"updater.exe /enable-logging "
+      L"/vmodule=*/components/update_client/*=2,*/chrome/updater/*=2 "
+      L"/handoff \"appguid={CDABE316-39CD-43BA-8440-6D1E0547AEE6}&lang=en\"");
+
+  EXPECT_TRUE(cmd_line);
+  EXPECT_TRUE(cmd_line->HasSwitch("enable-logging"));
+  EXPECT_TRUE(cmd_line->GetSwitchValueASCII("enable-logging").empty());
+  EXPECT_EQ(cmd_line->GetSwitchValueASCII("vmodule"),
+            "*/components/update_client/*=2,*/chrome/updater/*=2");
+  EXPECT_EQ(cmd_line->GetSwitchValueASCII("handoff"),
+            "appguid={CDABE316-39CD-43BA-8440-6D1E0547AEE6}&lang=en");
+}
+
 }  // namespace updater
diff --git a/chrome/updater/util/win_util.cc b/chrome/updater/util/win_util.cc
index a4dee5d..3e036c5 100644
--- a/chrome/updater/util/win_util.cc
+++ b/chrome/updater/util/win_util.cc
@@ -44,6 +44,7 @@
 #include "base/ranges/algorithm.h"
 #include "base/scoped_native_library.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
@@ -975,7 +976,13 @@
       VLOG(1) << "Empty switch in command line: [" << cmd_string << "]";
       return std::nullopt;
     }
-
+    if (base::StringPairs switch_value_pairs;
+        base::SplitStringIntoKeyValuePairs(switch_name, '=', '\n',
+                                           &switch_value_pairs)) {
+      command_line.AppendSwitchASCII(switch_value_pairs[0].first,
+                                     switch_value_pairs[0].second);
+      continue;
+    }
     if (is_legacy_switch(next_arg) || next_arg.empty()) {
       command_line.AppendSwitch(switch_name);
     } else {
diff --git a/chromeos/ash/components/growth/campaigns_model.h b/chromeos/ash/components/growth/campaigns_model.h
index 40013ed..27151bb 100644
--- a/chromeos/ash/components/growth/campaigns_model.h
+++ b/chromeos/ash/components/growth/campaigns_model.h
@@ -29,7 +29,7 @@
 
 // Entries should not be renumbered and numeric values should never be reused
 // as it is used for logging metrics as well. Please keep in sync with
-// "CampaignSlot" in src/tools/metrics/histograms/enums.xml.
+// "CampaignSlot" in tools/metrics/histograms/metadata/ash_growth/enums.xml.
 enum class Slot {
   kDemoModeApp = 0,
   kDemoModeFreePlayApps = 1,
diff --git a/chromeos/ash/components/system/fake_statistics_provider.cc b/chromeos/ash/components/system/fake_statistics_provider.cc
index a63eaad..a75ec8ec 100644
--- a/chromeos/ash/components/system/fake_statistics_provider.cc
+++ b/chromeos/ash/components/system/fake_statistics_provider.cc
@@ -70,6 +70,10 @@
   machine_statistics_.erase(key);
 }
 
+void FakeStatisticsProvider::ClearAllMachineStatistics() {
+  machine_statistics_.clear();
+}
+
 void FakeStatisticsProvider::SetMachineFlag(const std::string& key,
                                             bool value) {
   machine_flags_[key] = value;
diff --git a/chromeos/ash/components/system/fake_statistics_provider.h b/chromeos/ash/components/system/fake_statistics_provider.h
index a767be7b..fb5e92e 100644
--- a/chromeos/ash/components/system/fake_statistics_provider.h
+++ b/chromeos/ash/components/system/fake_statistics_provider.h
@@ -39,6 +39,7 @@
 
   void SetMachineStatistic(const std::string& key, const std::string& value);
   void ClearMachineStatistic(std::string_view key);
+  void ClearAllMachineStatistics();
   void SetMachineFlag(const std::string& key, bool value);
   void ClearMachineFlag(std::string_view key);
   void SetVpdStatus(VpdStatus new_status);
diff --git a/chromeos/ash/services/libassistant/chromium_http_connection.cc b/chromeos/ash/services/libassistant/chromium_http_connection.cc
index ce16ea3..45ce9c97 100644
--- a/chromeos/ash/services/libassistant/chromium_http_connection.cc
+++ b/chromeos/ash/services/libassistant/chromium_http_connection.cc
@@ -326,7 +326,7 @@
   if (!upload_pipe_.is_valid() || upload_body_.empty())
     return;
 
-  uint32_t write_bytes = upload_body_.size();
+  size_t write_bytes = upload_body_.size();
   MojoResult result = upload_pipe_->WriteData(upload_body_.data(), &write_bytes,
                                               MOJO_WRITE_DATA_FLAG_NONE);
 
diff --git a/chromeos/crosapi/mojom/test_controller.mojom b/chromeos/crosapi/mojom/test_controller.mojom
index 59d2306..c566182 100644
--- a/chromeos/crosapi/mojom/test_controller.mojom
+++ b/chromeos/crosapi/mojom/test_controller.mojom
@@ -48,6 +48,14 @@
   kTrue,
 };
 
+[Stable, Extensible]
+enum MachineStatisticKeyType {
+  [Default] kUnknown = 0,
+  kOemDeviceRequisitionKey = 1,
+  kHardwareClassKey = 2,
+  kCustomizationIdKey = 3,
+};
+
 // Dev mode IWAs are either installed from a proxy origin, or from a Web Bundle
 // file.
 [Stable]
@@ -336,8 +344,8 @@
 // This interface is implemented by Ash-Chrome.
 // This interface provides tests a mechanism to mutate or query ash.
 // In the future, this interface may merge with an automation or a11y interface.
-// Next version: 35
-// Next method id: 50
+// Next version: 36
+// Next method id: 53
 [Stable, Uuid="1f93f9d7-e466-466c-a675-c21b48cf30d3"]
 interface TestController {
   // Clicks the middle of the views element identified by |element_name|.
@@ -602,4 +610,15 @@
   // Sets the number of displays through the `DisplayManagerTestApi`.
   // The values can be in the range [1, 8].
   [MinVersion=34] UpdateDisplay@49(int32 number_of_displays) => ();
+
+  // Enables or disables FakeStatisticsProvider for testing.
+  [MinVersion=35] EnableStatisticsProviderForTesting@50(bool enable) => ();
+
+  // Clears all machine statistics data from FakeStatisficsProvider for testing.
+  [MinVersion=35] ClearAllMachineStatistics@51() => ();
+
+  // Sets the machine statistics data with FakeStatisficsProvider for testing.
+  // Returns true if succeeded.
+  [MinVersion=35] SetMachineStatistic@52(MachineStatisticKeyType key,
+      string value) => (bool success);
 };
diff --git a/clank b/clank
index ef9ed76..4ccf9ec 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit ef9ed7638a0a413170457f22032944d1b61febbb
+Subproject commit 4ccf9ec2cc92967d39ca0e3d0b0158bfd7937c81
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc
index d35380b..ef59a75 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -1999,7 +1999,8 @@
       // interactions.
       metadata_logging_context.instrument_ids_with_benefits_available.insert(
           credit_card.instrument_id());
-      if (credit_card.IsCardEligibleForBenefits()) {
+      if (personal_data().payments_data_manager().IsCardEligibleForBenefits(
+              credit_card)) {
         labels.push_back({*benefit_label});
       }
       suggestion.feature_for_iph =
@@ -2159,7 +2160,9 @@
       suggestion.labels = {};
       std::optional<Suggestion::Text> benefit_label =
           GetCreditCardBenefitSuggestionLabel(credit_card);
-      if (benefit_label && credit_card.IsCardEligibleForBenefits()) {
+      if (benefit_label &&
+          personal_data().payments_data_manager().IsCardEligibleForBenefits(
+              credit_card)) {
         suggestion.labels.push_back({*benefit_label});
       }
     }
diff --git a/components/autofill/core/browser/data_model/credit_card.cc b/components/autofill/core/browser/data_model/credit_card.cc
index 3ff6d31..a585dcd 100644
--- a/components/autofill/core/browser/data_model/credit_card.cc
+++ b/components/autofill/core/browser/data_model/credit_card.cc
@@ -1253,15 +1253,6 @@
          card_art_url().spec() != kCapitalOneCardArtUrl;
 }
 
-bool CreditCard::IsCardEligibleForBenefits() const {
-  return (issuer_id() == kAmexCardIssuerId &&
-          base::FeatureList::IsEnabled(
-              features::kAutofillEnableCardBenefitsForAmericanExpress)) ||
-         (issuer_id() == kCapitalOneCardIssuerId &&
-          base::FeatureList::IsEnabled(
-              features::kAutofillEnableCardBenefitsForCapitalOne));
-}
-
 void CreditCard::GetSupportedTypes(FieldTypeSet* supported_types) const {
   supported_types->insert(CREDIT_CARD_NAME_FULL);
   supported_types->insert(CREDIT_CARD_NAME_FIRST);
diff --git a/components/autofill/core/browser/data_model/credit_card.h b/components/autofill/core/browser/data_model/credit_card.h
index 97eb8293..b7860dc3 100644
--- a/components/autofill/core/browser/data_model/credit_card.h
+++ b/components/autofill/core/browser/data_model/credit_card.h
@@ -467,12 +467,6 @@
   // image.
   bool HasRichCardArtImageFromMetadata() const;
 
-  // Returns whether the card is from an issuer eligible for benefits and the
-  // user is in a benefits Chrome experiment for the card's issuer.
-  // TODO(crbug.com/330908547): Move IsCardEligibleForBenefits to the
-  // PaymentsDataManager.
-  bool IsCardEligibleForBenefits() const;
-
   const std::u16string& product_description() const {
     return product_description_;
   }
diff --git a/components/autofill/core/browser/payments/autofill_payments_feature_availability.cc b/components/autofill/core/browser/payments/autofill_payments_feature_availability.cc
index f7ab396..b066829 100644
--- a/components/autofill/core/browser/payments/autofill_payments_feature_availability.cc
+++ b/components/autofill/core/browser/payments/autofill_payments_feature_availability.cc
@@ -6,7 +6,7 @@
 
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/payments_data_manager.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 
 namespace autofill {
@@ -23,9 +23,9 @@
 bool DidDisplayBenefitForCard(
     const CreditCard& card,
     const AutofillClient& autofill_client,
-    const PersonalDataManager& personal_data_manager) {
-  return card.IsCardEligibleForBenefits() &&
-         !personal_data_manager.payments_data_manager()
+    const PaymentsDataManager& payments_data_manager) {
+  return payments_data_manager.IsCardEligibleForBenefits(card) &&
+         !payments_data_manager
               .GetApplicableBenefitDescriptionForCardAndOrigin(
                   card,
                   autofill_client.GetLastCommittedPrimaryMainFrameOrigin(),
diff --git a/components/autofill/core/browser/payments/autofill_payments_feature_availability.h b/components/autofill/core/browser/payments/autofill_payments_feature_availability.h
index e155301..f954dce 100644
--- a/components/autofill/core/browser/payments/autofill_payments_feature_availability.h
+++ b/components/autofill/core/browser/payments/autofill_payments_feature_availability.h
@@ -9,13 +9,13 @@
 
 class AutofillClient;
 class CreditCard;
-class PersonalDataManager;
+class PaymentsDataManager;
 
 // Returns whether the `card` is shown in an Autofill suggestion dropdown with a
 // benefit label.
 bool DidDisplayBenefitForCard(const CreditCard& card,
                               const AutofillClient& autofill_client,
-                              const PersonalDataManager& personal_data_manager);
+                              const PaymentsDataManager& payments_data_manager);
 
 // Returns whether the `card` is populated with a card art image and a card
 // product name and whether they both should be shown.
diff --git a/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc b/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
index 388cb37..c92df66 100644
--- a/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_otp_authenticator.cc
@@ -43,7 +43,8 @@
         ClientBehaviorConstants::kShowingCardArtImageAndCardProductName);
   }
   if (DidDisplayBenefitForCard(*card_, *autofill_client_,
-                               *autofill_client_->GetPersonalDataManager())) {
+                               autofill_client_->GetPersonalDataManager()
+                                   ->payments_data_manager())) {
     unmask_request_->client_behavior_signals.push_back(
         ClientBehaviorConstants::kShowingCardBenefits);
   }
diff --git a/components/autofill/core/browser/payments/credit_card_risk_based_authenticator.cc b/components/autofill/core/browser/payments/credit_card_risk_based_authenticator.cc
index d6bd751..d6aff830 100644
--- a/components/autofill/core/browser/payments/credit_card_risk_based_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_risk_based_authenticator.cc
@@ -70,7 +70,8 @@
   }
   if (DidDisplayBenefitForCard(unmask_request_details_->card,
                                autofill_client_.get(),
-                               *autofill_client_->GetPersonalDataManager())) {
+                               autofill_client_->GetPersonalDataManager()
+                                   ->payments_data_manager())) {
     unmask_request_details_->client_behavior_signals.push_back(
         ClientBehaviorConstants::kShowingCardBenefits);
   }
diff --git a/components/autofill/core/browser/payments/full_card_request.cc b/components/autofill/core/browser/payments/full_card_request.cc
index 88ad344..457a551e 100644
--- a/components/autofill/core/browser/payments/full_card_request.cc
+++ b/components/autofill/core/browser/payments/full_card_request.cc
@@ -177,8 +177,9 @@
   // TODO(crbug.com/332715322): Refactor FullCardRequest to use
   // AutofillClient::GetPersonalDataManager() instead of a separate class
   // variable.
-  if (DidDisplayBenefitForCard(card, autofill_client_.get(),
-                               *personal_data_manager_)) {
+  if (DidDisplayBenefitForCard(
+          card, autofill_client_.get(),
+          personal_data_manager_->payments_data_manager())) {
     request_->client_behavior_signals.push_back(
         ClientBehaviorConstants::kShowingCardBenefits);
   }
diff --git a/components/autofill/core/browser/payments_data_manager.cc b/components/autofill/core/browser/payments_data_manager.cc
index 1d61a58b..b1431b6 100644
--- a/components/autofill/core/browser/payments_data_manager.cc
+++ b/components/autofill/core/browser/payments_data_manager.cc
@@ -23,6 +23,7 @@
 #include "components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h"
 #include "components/autofill/core/browser/metrics/payments/offers_metrics.h"
 #include "components/autofill/core/browser/metrics/payments/wallet_usage_data_metrics.h"
+#include "components/autofill/core/browser/payments/constants.h"
 #include "components/autofill/core/browser/payments/payments_data_cleaner.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/ui/autofill_image_fetcher_base.h"
@@ -862,6 +863,16 @@
           base::Unretained(this)));
 }
 
+bool PaymentsDataManager::IsCardEligibleForBenefits(
+    const CreditCard& card) const {
+  return (card.issuer_id() == kAmexCardIssuerId &&
+          base::FeatureList::IsEnabled(
+              features::kAutofillEnableCardBenefitsForAmericanExpress)) ||
+         (card.issuer_id() == kCapitalOneCardIssuerId &&
+          base::FeatureList::IsEnabled(
+              features::kAutofillEnableCardBenefitsForCapitalOne));
+}
+
 bool PaymentsDataManager::IsCardBenefitsFeatureEnabled() {
   return base::FeatureList::IsEnabled(
              features::kAutofillEnableCardBenefitsForAmericanExpress) ||
diff --git a/components/autofill/core/browser/payments_data_manager.h b/components/autofill/core/browser/payments_data_manager.h
index c63d595..4238c22 100644
--- a/components/autofill/core/browser/payments_data_manager.h
+++ b/components/autofill/core/browser/payments_data_manager.h
@@ -294,6 +294,10 @@
   // present in the cache, this function will return a nullptr.
   gfx::Image* GetCachedCardArtImageForUrl(const GURL& card_art_url) const;
 
+  // Checks if a specific card is eligible to see benefits based on its issuer
+  // id.
+  bool IsCardEligibleForBenefits(const CreditCard& card) const;
+
   // Checks if the user is in an experiment for seeing credit card benefits in
   // Autofill suggestions.
   bool IsCardBenefitsFeatureEnabled();
diff --git a/components/autofill/core/common/autofill_prefs.cc b/components/autofill/core/common/autofill_prefs.cc
index 054cd27a..0add69aa 100644
--- a/components/autofill/core/common/autofill_prefs.cc
+++ b/components/autofill/core/common/autofill_prefs.cc
@@ -106,6 +106,12 @@
   registry->RegisterBooleanPref(prefs::kAutofillUsingVirtualViewStructure,
                                 false);
 #endif
+
+#if BUILDFLAG(IS_ANDROID)
+  registry->RegisterBooleanPref(
+      prefs::kFacilitatedPaymentsPix, /*default_value=*/true,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+#endif
 }
 
 void MigrateDeprecatedAutofillPrefs(PrefService* pref_service) {
diff --git a/components/autofill/core/common/autofill_prefs.h b/components/autofill/core/common/autofill_prefs.h
index c5e8ee3..10a44f2 100644
--- a/components/autofill/core/common/autofill_prefs.h
+++ b/components/autofill/core/common/autofill_prefs.h
@@ -115,6 +115,10 @@
     "autofill.using_virtual_view_structure";
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+inline constexpr char kFacilitatedPaymentsPix[] = "facilitated_payments.pix";
+#endif  // BUILDFLAG(IS_ANDROID)
+
 // The maximum value for the
 // `kAutofillPaymentMethodsMandatoryReauthPromoShownCounter` pref. If this
 // value is reached, we should not show a mandatory re-auth promo.
@@ -194,6 +198,10 @@
 
 bool UsesVirtualViewStructureForAutofill(const PrefService* prefs);
 
+void SetFacilitatedPaymentsPix(PrefService* prefs, bool value);
+
+bool IsFacilitatedPaymentsPixEnabled(const PrefService* prefs);
+
 }  // namespace autofill::prefs
 
 #endif  // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PREFS_H_
diff --git a/components/autofill/core/common/autofill_prefs_unittest.cc b/components/autofill/core/common/autofill_prefs_unittest.cc
index ef9c8cf..000e177 100644
--- a/components/autofill/core/common/autofill_prefs_unittest.cc
+++ b/components/autofill/core/common/autofill_prefs_unittest.cc
@@ -156,5 +156,11 @@
   EXPECT_EQ(dictionary, *base::JSONReader::Read(output_js));
 }
 
+#if BUILDFLAG(IS_ANDROID)
+TEST_F(AutofillPrefsTest, FacilitatedPaymentsPixPref_DefaultValueSetToTrue) {
+  EXPECT_TRUE(pref_service()->GetBoolean(prefs::kFacilitatedPaymentsPix));
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
 }  // namespace prefs
 }  // namespace autofill
diff --git a/components/cast_streaming/browser/cast_message_port_impl_unittest.cc b/components/cast_streaming/browser/cast_message_port_impl_unittest.cc
index cefd968..9689526 100644
--- a/components/cast_streaming/browser/cast_message_port_impl_unittest.cc
+++ b/components/cast_streaming/browser/cast_message_port_impl_unittest.cc
@@ -86,7 +86,7 @@
       std::move(receiver_message_closure_).Run();
     }
   }
-  void OnError(openscreen::Error error) override {
+  void OnError(const openscreen::Error& error) override {
     latest_error_ = error;
     if (error_closure_) {
       std::move(error_closure_).Run();
diff --git a/components/cast_streaming/browser/cast_streaming_session.cc b/components/cast_streaming/browser/cast_streaming_session.cc
index 0c1c4c8..369b263 100644
--- a/components/cast_streaming/browser/cast_streaming_session.cc
+++ b/components/cast_streaming/browser/cast_streaming_session.cc
@@ -431,7 +431,7 @@
 
 void CastStreamingSession::ReceiverSessionClient::OnError(
     const openscreen::cast::ReceiverSession* session,
-    openscreen::Error error) {
+    const openscreen::Error& error) {
   DCHECK_EQ(session, receiver_session_.get());
   LOG(ERROR) << error;
   if (!is_initialized_) {
diff --git a/components/cast_streaming/browser/cast_streaming_session.h b/components/cast_streaming/browser/cast_streaming_session.h
index 3099b9f..3d99a70 100644
--- a/components/cast_streaming/browser/cast_streaming_session.h
+++ b/components/cast_streaming/browser/cast_streaming_session.h
@@ -188,7 +188,7 @@
     void OnReceiversDestroying(const openscreen::cast::ReceiverSession* session,
                                ReceiversDestroyingReason reason) override;
     void OnError(const openscreen::cast::ReceiverSession* session,
-                 openscreen::Error error) override;
+                 const openscreen::Error& error) override;
 
     void OnDataTimeout();
     void OnCastChannelClosed();
diff --git a/components/cast_streaming/test/cast_streaming_test_sender.cc b/components/cast_streaming/test/cast_streaming_test_sender.cc
index 5c9756d..fd3d4cf 100644
--- a/components/cast_streaming/test/cast_streaming_test_sender.cc
+++ b/components/cast_streaming/test/cast_streaming_test_sender.cc
@@ -264,7 +264,7 @@
 
 void CastStreamingTestSender::OnError(
     const openscreen::cast::SenderSession* session,
-    openscreen::Error error) {
+    const openscreen::Error& error) {
   LOG(ERROR) << "Sender Session error: " << error.ToString();
   CHECK_EQ(session, sender_session_.get());
   Stop();
diff --git a/components/cast_streaming/test/cast_streaming_test_sender.h b/components/cast_streaming/test/cast_streaming_test_sender.h
index 6ac2eed..baa5c17 100644
--- a/components/cast_streaming/test/cast_streaming_test_sender.h
+++ b/components/cast_streaming/test/cast_streaming_test_sender.h
@@ -99,7 +99,7 @@
                     openscreen::cast::capture_recommendations::Recommendations
                         capture_recommendations) final;
   void OnError(const openscreen::cast::SenderSession* session,
-               openscreen::Error error) final;
+               const openscreen::Error& error) final;
 
   openscreen_platform::TaskRunner task_runner_;
   openscreen::cast::Environment environment_;
diff --git a/components/commerce_strings.grdp b/components/commerce_strings.grdp
index 918de57..2b5083b 100644
--- a/components/commerce_strings.grdp
+++ b/components/commerce_strings.grdp
@@ -297,6 +297,11 @@
       </message>
     </if> <!-- not use_titlecase -->
 
+    <!-- For Desktop Product Specifications -->
+    <message name="IDS_PRODUCT_SPECIFICATIONS_OPEN_TABS_SECTION" translateable="false" desc="The title of the section in the product selection drop-down in the product specifications UI that shows a list of open tabs.">
+      Open Tabs
+    </message>
+
     <message name="IDS_SHOPPING_INSIGHTS_BUYING_OPTIONS" desc="Text shown in Shopping Insights side panel. Clicking this link will bring users to a new tab where users can check price insights of all available buying options.">
       Buying options
     </message>
diff --git a/components/embedder_support/ios/BUILD.gn b/components/embedder_support/ios/BUILD.gn
new file mode 100644
index 0000000..d028990a
--- /dev/null
+++ b/components/embedder_support/ios/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("web_contents_delegate") {
+  sources = [
+    "delegate/color_chooser/color_chooser_consumer_ios.h",
+    "delegate/color_chooser/color_chooser_controller_ios.h",
+    "delegate/color_chooser/color_chooser_controller_ios.mm",
+    "delegate/color_chooser/color_chooser_coordinator_ios.h",
+    "delegate/color_chooser/color_chooser_coordinator_ios.mm",
+    "delegate/color_chooser/color_chooser_delegate_ios.h",
+    "delegate/color_chooser/color_chooser_ios.h",
+    "delegate/color_chooser/color_chooser_ios.mm",
+    "delegate/color_chooser/color_chooser_mediator_ios.h",
+    "delegate/color_chooser/color_chooser_mediator_ios.mm",
+  ]
+
+  deps = [
+    "//base",
+    "//content/public/browser",
+    "//content/public/common",
+    "//skia",
+    "//ui/base",
+    "//ui/gfx",
+    "//url",
+  ]
+}
diff --git a/components/embedder_support/ios/DEPS b/components/embedder_support/ios/DEPS
new file mode 100644
index 0000000..df385638
--- /dev/null
+++ b/components/embedder_support/ios/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+skia/ext/skia_utils_ios.h",
+  "+third_party/blink/public/mojom/choosers/color_chooser.mojom.h",
+  "+third_party/skia/include/core/SkColor.h",
+  "+ui/gfx/native_widget_types.h",
+]
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_consumer_ios.h b/components/embedder_support/ios/delegate/color_chooser/color_chooser_consumer_ios.h
new file mode 100644
index 0000000..01967b14
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_consumer_ios.h
@@ -0,0 +1,23 @@
+// 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_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_CONSUMER_IOS_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_CONSUMER_IOS_H_
+
+#import <Foundation/Foundation.h>
+
+@class UIColor;
+
+// Consumer for model to request actions to the color chooser UI.
+@protocol ColorChooserConsumerIOS <NSObject>
+
+// Closes the color chooser.
+- (void)closeColorChooser;
+
+// Sets the selected color in the color chooser with `color`.
+- (void)setColor:(UIColor*)color;
+
+@end
+
+#endif  // COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_CONSUMER_IOS_H_
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_controller_ios.h b/components/embedder_support/ios/delegate/color_chooser/color_chooser_controller_ios.h
new file mode 100644
index 0000000..bab59aa
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_controller_ios.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_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_CONTROLLER_IOS_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_CONTROLLER_IOS_H_
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_consumer_ios.h"
+
+@protocol ColorChooserDelegateIOS;
+
+// The color chooser controller that is opened as a popover.
+@interface ColorChooserControllerIOS
+    : UIColorPickerViewController <ColorChooserConsumerIOS,
+                                   UIColorPickerViewControllerDelegate,
+                                   UIPopoverPresentationControllerDelegate>
+
+// The view controller this coordinator was initialized with.
+@property(weak, nonatomic, readonly) UIViewController* baseViewController;
+
+// Delegate used to handle the actions in the color chooser.
+@property(nonatomic, weak) id<ColorChooserDelegateIOS> chooserDelegate;
+
+// Initializer.
+- (instancetype)initWithChooserDelegate:(id<ColorChooserDelegateIOS>)delegate
+                                  color:(UIColor*)color;
+
+@end
+
+#endif  // COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_CONTROLLER_IOS_H_
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_controller_ios.mm b/components/embedder_support/ios/delegate/color_chooser/color_chooser_controller_ios.mm
new file mode 100644
index 0000000..f2f2eb1
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_controller_ios.mm
@@ -0,0 +1,42 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_controller_ios.h"
+
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_delegate_ios.h"
+
+@implementation ColorChooserControllerIOS
+- (instancetype)initWithChooserDelegate:
+                    (id<ColorChooserDelegateIOS>)chooserDelegate
+                                  color:(UIColor*)color {
+  if (!(self = [super init])) {
+    return nil;
+  }
+
+  _chooserDelegate = chooserDelegate;
+  [self setDelegate:self];
+  [self setColor:color];
+  [self setSupportsAlpha:FALSE];
+  return self;
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+  // Update the selected color here when UIColorPickerViewController is closed
+  // by tapping the close button or swiping down the popover.
+  // `UIColorPickerViewControllerDelegate::colorPickerViewControllerDidFinish`
+  // is called only when a user taps the close button.
+  [self.chooserDelegate onColorChosen:self selectedColor:self.selectedColor];
+}
+
+#pragma mark - ColorChooserConsumerIOS
+
+- (void)closeColorChooser {
+  [self dismissViewControllerAnimated:YES completion:nil];
+}
+
+- (void)setColor:(UIColor*)color {
+  [self setSelectedColor:color];
+}
+
+@end
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_coordinator_ios.h b/components/embedder_support/ios/delegate/color_chooser/color_chooser_coordinator_ios.h
new file mode 100644
index 0000000..89a9550
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_coordinator_ios.h
@@ -0,0 +1,44 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_COORDINATOR_IOS_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_COORDINATOR_IOS_H_
+
+#import <Foundation/Foundation.h>
+
+namespace web_contents_delegate_ios {
+class ColorChooserIOS;
+}
+
+@class ColorChooserControllerIOS;
+@class ColorChooserMediatorIOS;
+@class UIViewController;
+@class UIColor;
+
+// Holds a controller and a mediator so that communicates the controller and
+// content implementations.
+@interface ColorChooserCoordinatorIOS : NSObject
+
+// The controller that has UI components.
+@property(nonatomic, strong) ColorChooserControllerIOS* colorChooserController;
+
+// The mediator that implements ColorChooserDelegateIOS.
+@property(nonatomic, strong) ColorChooserMediatorIOS* colorChooserMediator;
+
+// Initializer.
+- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
+                              colorChooser:
+                                  (web_contents_delegate_ios::ColorChooserIOS*)
+                                      colorChooser
+                                     color:(UIColor*)color;
+
+// Closes the color chooser.
+- (void)closeColorChooser;
+
+// Sets the selected color in the color chooser.
+- (void)setColor:(UIColor*)color;
+
+@end
+
+#endif  // COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_COORDINATOR_IOS_H_
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_coordinator_ios.mm b/components/embedder_support/ios/delegate/color_chooser/color_chooser_coordinator_ios.mm
new file mode 100644
index 0000000..ec7fe9df
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_coordinator_ios.mm
@@ -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.
+
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_coordinator_ios.h"
+
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_controller_ios.h"
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.h"
+#import "ui/gfx/native_widget_types.h"
+
+@implementation ColorChooserCoordinatorIOS
+
+- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
+                              colorChooser:
+                                  (web_contents_delegate_ios::ColorChooserIOS*)
+                                      colorChooser
+                                     color:(UIColor*)color {
+  if (!(self = [super init])) {
+    return nil;
+  }
+
+  _colorChooserMediator =
+      [[ColorChooserMediatorIOS alloc] initWithColorChooser:colorChooser];
+
+  _colorChooserController = [[ColorChooserControllerIOS alloc]
+      initWithChooserDelegate:_colorChooserMediator
+                        color:color];
+  _colorChooserController.modalInPresentation = YES;
+  _colorChooserController.modalPresentationStyle = UIModalPresentationPopover;
+  _colorChooserController.popoverPresentationController.delegate =
+      _colorChooserController;
+  _colorChooserController.popoverPresentationController.sourceView =
+      baseViewController.view;
+  _colorChooserController.popoverPresentationController.sourceRect =
+      baseViewController.view.bounds;
+
+  _colorChooserMediator.consumer = _colorChooserController;
+  [baseViewController presentViewController:_colorChooserController
+                                   animated:YES
+                                 completion:nil];
+  return self;
+}
+
+- (void)closeColorChooser {
+  [self.colorChooserMediator closeColorChooser];
+}
+
+- (void)setColor:(UIColor*)color {
+  [self.colorChooserMediator setColor:color];
+}
+
+@end
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_delegate_ios.h b/components/embedder_support/ios/delegate/color_chooser/color_chooser_delegate_ios.h
new file mode 100644
index 0000000..92047b1
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_delegate_ios.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 COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_DELEGATE_IOS_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_DELEGATE_IOS_H_
+
+#import <Foundation/Foundation.h>
+
+@class ColorChooserControllerIOS;
+@class UIColor;
+
+// Delegate to handle actions.
+@protocol ColorChooserDelegateIOS <NSObject>
+
+// Method invoked when the user closes the UIColorPickerViewController.
+- (void)onColorChosen:(ColorChooserControllerIOS*)controller
+        selectedColor:(UIColor*)color;
+
+@end
+
+#endif  // COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_DELEGATE_IOS_H_
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.h b/components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.h
new file mode 100644
index 0000000..9c81213
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.h
@@ -0,0 +1,55 @@
+// 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_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_IOS_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_IOS_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/raw_ptr.h"
+#include "content/public/browser/color_chooser.h"
+#include "third_party/blink/public/mojom/choosers/color_chooser.mojom.h"
+
+namespace content {
+class WebContents;
+}
+
+@class ColorChooserCoordinatorIOS;
+
+namespace web_contents_delegate_ios {
+
+class ColorChooserIOS : public content::ColorChooser {
+ public:
+  ColorChooserIOS(
+      content::WebContents* web_contents,
+      SkColor initial_color,
+      const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions);
+
+  ColorChooserIOS(const ColorChooserIOS&) = delete;
+  ColorChooserIOS& operator=(const ColorChooserIOS&) = delete;
+
+  ~ColorChooserIOS() override;
+
+  void OnColorChosen(SkColor color);
+
+  // content::ColorChooser interface
+  void End() override;
+  void SetSelectedColor(SkColor color) override;
+
+  // Return a weak pointer to the current object.
+  base::WeakPtr<ColorChooserIOS> AsWeakPtr();
+
+ private:
+  // The web contents invoking the color chooser.  No ownership because it will
+  // outlive this class.
+  raw_ptr<content::WebContents> web_contents_;
+  ColorChooserCoordinatorIOS* __strong coordinator_;
+
+  base::WeakPtrFactory<ColorChooserIOS> weak_ptr_factory_{this};
+};
+
+}  // namespace web_contents_delegate_ios
+
+#endif  // COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_IOS_H_
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.mm b/components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.mm
new file mode 100644
index 0000000..7fe0bc81
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.mm
@@ -0,0 +1,59 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.h"
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+#include "components/embedder_support/ios/delegate/color_chooser/color_chooser_coordinator_ios.h"
+#include "components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.h"
+#include "content/public/browser/web_contents.h"
+#include "skia/ext/skia_utils_ios.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace web_contents_delegate_ios {
+
+ColorChooserIOS::ColorChooserIOS(
+    content::WebContents* web_contents,
+    SkColor initial_color,
+    const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions)
+    : web_contents_(web_contents) {
+  gfx::NativeWindow native_window = web_contents_->GetTopLevelNativeWindow();
+  coordinator_ = [[ColorChooserCoordinatorIOS alloc]
+      initWithBaseViewController:native_window.Get().rootViewController
+                    colorChooser:this
+                           color:skia::UIColorFromSkColor(initial_color)];
+}
+
+ColorChooserIOS::~ColorChooserIOS() {}
+
+void ColorChooserIOS::End() {
+  if (!coordinator_) {
+    return;
+  }
+  [coordinator_ closeColorChooser];
+  coordinator_ = nullptr;
+}
+
+void ColorChooserIOS::SetSelectedColor(SkColor color) {
+  if (!coordinator_) {
+    return;
+  }
+  [coordinator_ setColor:skia::UIColorFromSkColor(color)];
+}
+
+void ColorChooserIOS::OnColorChosen(SkColor color) {
+  // Clean up |coordinator_| since this is called after the UI chooser is
+  // closed.
+  coordinator_ = nullptr;
+  web_contents_->DidChooseColorInColorChooser(color);
+  web_contents_->DidEndColorChooser();
+}
+
+base::WeakPtr<ColorChooserIOS> ColorChooserIOS::AsWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+}  // namespace web_contents_delegate_ios
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.h b/components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.h
new file mode 100644
index 0000000..555681e4
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.h
@@ -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.
+
+#ifndef COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_MEDIATOR_IOS_H_
+#define COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_MEDIATOR_IOS_H_
+
+#import <Foundation/Foundation.h>
+
+#import "base/memory/weak_ptr.h"
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_delegate_ios.h"
+
+namespace web_contents_delegate_ios {
+class ColorChooserIOS;
+}  // namespace web_contents_delegate_ios
+
+@protocol ColorChooserConsumerIOS;
+@class UIColor;
+
+// The mediator that implements ColorChooserDelegateIOS. It also communicates
+// with the controller, referring to `consumer`.
+@interface ColorChooserMediatorIOS : NSObject <ColorChooserDelegateIOS>
+
+@property(nonatomic, assign)
+    base::WeakPtr<web_contents_delegate_ios::ColorChooserIOS>
+        colorChooser;
+
+// Consumer that is configured by this mediator.
+@property(nonatomic, assign) id<ColorChooserConsumerIOS> consumer;
+
+// Initializer.
+- (instancetype)initWithColorChooser:
+    (web_contents_delegate_ios::ColorChooserIOS*)colorChooser;
+
+// Closes the color chooser.
+- (void)closeColorChooser;
+
+// Sets the selected color in the color chooser.
+- (void)setColor:(UIColor*)color;
+
+@end
+
+#endif  // COMPONENTS_EMBEDDER_SUPPORT_IOS_DELEGATE_COLOR_CHOOSER_COLOR_CHOOSER_MEDIATOR_IOS_H_
diff --git a/components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.mm b/components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.mm
new file mode 100644
index 0000000..2fd2c70
--- /dev/null
+++ b/components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.mm
@@ -0,0 +1,42 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_mediator_ios.h"
+
+#import <UIKit/UIKit.h>
+
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_consumer_ios.h"
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.h"
+#import "third_party/skia/include/core/SkColor.h"
+
+@implementation ColorChooserMediatorIOS
+
+- (instancetype)initWithColorChooser:
+    (web_contents_delegate_ios::ColorChooserIOS*)colorChooser {
+  if (!(self = [super init])) {
+    return nil;
+  }
+  _colorChooser = colorChooser->AsWeakPtr();
+  return self;
+}
+
+- (void)closeColorChooser {
+  [self.consumer closeColorChooser];
+}
+
+- (void)setColor:(UIColor*)color {
+  [self.consumer setColor:color];
+}
+
+#pragma mark - ColorChooserDelegateIOS
+
+- (void)onColorChosen:(ColorChooserControllerIOS*)controller
+        selectedColor:(UIColor*)color {
+  CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
+  [color getRed:&red green:&green blue:&blue alpha:&alpha];
+  self.colorChooser->OnColorChosen(SkColorSetARGB(
+      alpha * 255.0f, red * 255.0f, green * 255.0f, blue * 255.0f));
+}
+
+@end
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc
index 285ac9e4..1709564 100644
--- a/components/history_clusters/core/config.cc
+++ b/components/history_clusters/core/config.cc
@@ -71,21 +71,6 @@
             sort_clusters_within_batch_for_query);
   }
 
-  // The `kJourneysLabels` feature and child params.
-  {
-    labels_from_hostnames = GetFieldTrialParamByFeatureAsBool(
-        internal::kJourneysLabels, "labels_from_hostnames",
-        labels_from_hostnames);
-
-    labels_from_entities = GetFieldTrialParamByFeatureAsBool(
-        internal::kJourneysLabels, "labels_from_entities",
-        labels_from_entities);
-
-    labels_from_search_visit_entities = GetFieldTrialParamByFeatureAsBool(
-        internal::kJourneysLabels, "labels_from_search_visit_entities",
-        labels_from_search_visit_entities);
-  }
-
   // The `kJourneysImages` feature.
   {
     images = base::FeatureList::IsEnabled(internal::kJourneysImages);
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h
index 9573b67..96ec1931 100644
--- a/components/history_clusters/core/config.h
+++ b/components/history_clusters/core/config.h
@@ -63,23 +63,6 @@
   // reverse chronologically, but the clusters within batches will be resorted.
   bool sort_clusters_within_batch_for_query = false;
 
-  // The `kJourneysLabels` feature and child params.
-
-  // Whether to assign labels to clusters from the hostnames of the cluster.
-  // Does nothing if `should_label_clusters` is false. Note that since every
-  // cluster has a hostname, this flag in conjunction with
-  // `should_label_clusters` will give every cluster a label.
-  bool labels_from_hostnames = true;
-
-  // Whether to assign labels to clusters from the Entities of the cluster.
-  // Does nothing if `should_label_clusters` is false.
-  bool labels_from_entities = false;
-
-  // Whether to assign labels to clusters from the entities associated with
-  // search visits within a cluster if there are multiple search visits for the
-  // cluster.
-  bool labels_from_search_visit_entities = false;
-
   // The `kJourneysImages` feature and child params.
 
   // Whether to attempt to provide images for eligible Journeys.
diff --git a/components/history_clusters/core/features.cc b/components/history_clusters/core/features.cc
index c2696b6..b15074c 100644
--- a/components/history_clusters/core/features.cc
+++ b/components/history_clusters/core/features.cc
@@ -29,10 +29,6 @@
 
 BASE_FEATURE(kJourneys, "Journeys", enabled_by_default_desktop_only);
 
-BASE_FEATURE(kJourneysLabels,
-             "JourneysLabel",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kJourneysImages,
              "JourneysImages",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/history_clusters/core/features.h b/components/history_clusters/core/features.h
index ad7f6141..ee16c95 100644
--- a/components/history_clusters/core/features.h
+++ b/components/history_clusters/core/features.h
@@ -22,9 +22,6 @@
 // directly. Instead use `IsJourneysEnabled()` for the system language filter.
 BASE_DECLARE_FEATURE(kJourneys);
 
-// Enables labelling of Journeys in UI.
-BASE_DECLARE_FEATURE(kJourneysLabels);
-
 // Enables images for Journeys in UI.
 BASE_DECLARE_FEATURE(kJourneysImages);
 
diff --git a/components/history_clusters/core/label_cluster_finalizer.cc b/components/history_clusters/core/label_cluster_finalizer.cc
index 004406e7..ea57ddf 100644
--- a/components/history_clusters/core/label_cluster_finalizer.cc
+++ b/components/history_clusters/core/label_cluster_finalizer.cc
@@ -46,7 +46,7 @@
   }
 
   // If we haven't found a label yet, use hostnames if the flag is enabled.
-  if (GetConfig().labels_from_hostnames && !current_highest_scoring_label) {
+  if (!current_highest_scoring_label) {
     base::flat_map<std::u16string, float> hostname_to_score;
     for (const auto& visit : cluster.visits) {
       std::u16string host =
diff --git a/components/history_clusters/core/label_cluster_finalizer_unittest.cc b/components/history_clusters/core/label_cluster_finalizer_unittest.cc
index 8ff3c3c..ad0824c 100644
--- a/components/history_clusters/core/label_cluster_finalizer_unittest.cc
+++ b/components/history_clusters/core/label_cluster_finalizer_unittest.cc
@@ -56,26 +56,6 @@
       {"baz", 25}, {"someotherentity", 10}};
 
   {
-    // With only search term labelling active, there should be no label.
-    Config config;
-    config.labels_from_hostnames = false;
-    config.labels_from_entities = false;
-    SetConfigForTesting(config);
-
-    history::Cluster cluster;
-    cluster.visits = {visit2, visit3};
-    FinalizeCluster(cluster);
-    EXPECT_EQ(cluster.raw_label, std::nullopt);
-    EXPECT_EQ(cluster.label, std::nullopt);
-    EXPECT_EQ(cluster.label_source, LabelSource::kUnknown);
-  }
-
-  {
-    // With hostname labelling active only, we should use the hostname.
-    Config config;
-    config.labels_from_hostnames = true;
-    SetConfigForTesting(config);
-
     history::Cluster cluster;
     cluster.visits = {visit2, visit3};
     FinalizeCluster(cluster);
@@ -86,20 +66,11 @@
 }
 
 TEST_F(LabelClusterFinalizerTest, TakesHighestScoringSearchTermIfAvailable) {
-  // Verify that search terms take precedence even if labels from entities are
-  // enabled.
-  Config config;
-  config.labels_from_hostnames = true;
-  config.labels_from_entities = true;
-  config.labels_from_search_visit_entities = false;
-  SetConfigForTesting(config);
-
+  // Verify that search terms take precedence.
   history::ClusterVisit visit =
       testing::CreateClusterVisit(testing::CreateDefaultAnnotatedVisit(
           2, GURL("https://nosearchtermsbuthighscorevisit.com/")));
   visit.engagement_score = 0.9;
-  visit.annotated_visit.content_annotations.model_annotations.entities = {
-      {"github", 100}, {"onlyinnoisyvisit", 99}};
 
   history::ClusterVisit visit2 =
       testing::CreateClusterVisit(testing::CreateDefaultAnnotatedVisit(
@@ -110,8 +81,6 @@
   history::ClusterVisit visit3 = testing::CreateClusterVisit(
       testing::CreateDefaultAnnotatedVisit(2, GURL("https://baz.com/")));
   visit3.score = 0.8;
-  visit3.annotated_visit.content_annotations.model_annotations.entities = {
-      {"github", 100}, {"someotherentity", 100}};
   visit3.annotated_visit.content_annotations.search_terms = u"searchtermlabel";
 
   history::Cluster cluster;
diff --git a/components/js_injection/browser/js_communication_host.cc b/components/js_injection/browser/js_communication_host.cc
index c0eb2bb3..dd24977 100644
--- a/components/js_injection/browser/js_communication_host.cc
+++ b/components/js_injection/browser/js_communication_host.cc
@@ -138,7 +138,7 @@
     std::unique_ptr<WebMessageHostFactory> factory,
     const std::u16string& js_object_name,
     const std::vector<std::string>& allowed_origin_rules) {
-  // TODO(crbug.com/331250164): Cancel all bfcached / prerendered pages when
+  // TODO(crbug.com/331250164): Cancel all prerendered pages when
   // addWebMessageListener() is called.
 
   OriginMatcher origin_matcher;
@@ -156,6 +156,13 @@
   js_objects_.push_back(std::make_unique<JsObject>(
       js_object_name, origin_matcher, std::move(factory)));
 
+  // If a new message listener is added when a page is in BFCache, the
+  // listener won't be available when be page is restored since it is
+  // not injected for the page. To avoid this behavior difference when
+  // BFCache is involved vs not, evict all BFCached pages so that we won't
+  // restore any pages that don't have this object injected.
+  web_contents()->GetController().GetBackForwardCache().Flush();
+
   ForEachRenderFrameHostWithinSameWebContents(
       web_contents()->GetPrimaryMainFrame(),
       [this](content::RenderFrameHost* render_frame_host) {
diff --git a/components/js_injection/browser/js_to_browser_messaging.cc b/components/js_injection/browser/js_to_browser_messaging.cc
index 4e64e30..4dc0562 100644
--- a/components/js_injection/browser/js_to_browser_messaging.cc
+++ b/components/js_injection/browser/js_to_browser_messaging.cc
@@ -203,8 +203,6 @@
   // TODO(crbug.com/40752101): this should really call
   // IsInactiveAndDisallowReactivation().
 
-  // TODO(crbug.com/331250166): If an associated page is bfcached, evict it.
-
   // A RenderFrame may inject JsToBrowserMessaging in the JavaScript context
   // more than once because of reusing of RenderFrame.
   host_.reset();
diff --git a/components/mirroring/service/openscreen_message_port_unittest.cc b/components/mirroring/service/openscreen_message_port_unittest.cc
index 4b61dbe..9af8b0e 100644
--- a/components/mirroring/service/openscreen_message_port_unittest.cc
+++ b/components/mirroring/service/openscreen_message_port_unittest.cc
@@ -32,7 +32,7 @@
               OnMessage,
               (const std::string&, const std::string&, const std::string&),
               (override));
-  MOCK_METHOD(void, OnError, (openscreen::Error), (override));
+  MOCK_METHOD(void, OnError, (const openscreen::Error&), (override));
 
   const std::string& source_id() override { return source_id_; }
 
diff --git a/components/mirroring/service/openscreen_session_host.cc b/components/mirroring/service/openscreen_session_host.cc
index 380310a..9d1a5fb5 100644
--- a/components/mirroring/service/openscreen_session_host.cc
+++ b/components/mirroring/service/openscreen_session_host.cc
@@ -613,7 +613,7 @@
 
 void OpenscreenSessionHost::OnError(
     const openscreen::cast::SenderSession* session,
-    openscreen::Error error) {
+    const openscreen::Error& error) {
   switch (error.code()) {
     case openscreen::Error::Code::kAnswerTimeout:
       ReportAndLogError(SessionError::ANSWER_TIME_OUT, error.ToString());
diff --git a/components/mirroring/service/openscreen_session_host.h b/components/mirroring/service/openscreen_session_host.h
index f1fa323..b2461a18 100644
--- a/components/mirroring/service/openscreen_session_host.h
+++ b/components/mirroring/service/openscreen_session_host.h
@@ -111,7 +111,7 @@
       const openscreen::cast::SenderSession* session,
       openscreen::cast::RemotingCapabilities capabilities) override;
   void OnError(const openscreen::cast::SenderSession* session,
-               openscreen::Error error) override;
+               const openscreen::Error& error) override;
 
   // RtpStreamClient overrides.
   void OnError(const std::string& message) override;
diff --git a/components/openscreen_platform/message_port_tls_connection_unittest.cc b/components/openscreen_platform/message_port_tls_connection_unittest.cc
index fc25719..fdf8d85 100644
--- a/components/openscreen_platform/message_port_tls_connection_unittest.cc
+++ b/components/openscreen_platform/message_port_tls_connection_unittest.cc
@@ -42,7 +42,8 @@
   ~MockTlsConnectionClient() override = default;
 
   MOCK_METHOD2(OnRead, void(openscreen::TlsConnection*, std::vector<uint8_t>));
-  MOCK_METHOD2(OnError, void(openscreen::TlsConnection*, openscreen::Error));
+  MOCK_METHOD2(OnError,
+               void(openscreen::TlsConnection*, const openscreen::Error&));
 };
 
 class MockTaskRunner : public openscreen::TaskRunner {
diff --git a/components/openscreen_platform/tls_client_connection_unittest.cc b/components/openscreen_platform/tls_client_connection_unittest.cc
index 81b9fcc..aa2f6cd0 100644
--- a/components/openscreen_platform/tls_client_connection_unittest.cc
+++ b/components/openscreen_platform/tls_client_connection_unittest.cc
@@ -130,7 +130,7 @@
 
 class MockTlsConnectionClient : public TlsConnection::Client {
  public:
-  MOCK_METHOD(void, OnError, (TlsConnection*, Error), (override));
+  MOCK_METHOD(void, OnError, (TlsConnection*, const Error&), (override));
   MOCK_METHOD(void, OnRead, (TlsConnection*, std::vector<uint8_t>), (override));
 };
 
diff --git a/components/openscreen_platform/tls_connection_factory_unittest.cc b/components/openscreen_platform/tls_connection_factory_unittest.cc
index e34efae8..140cf0e 100644
--- a/components/openscreen_platform/tls_connection_factory_unittest.cc
+++ b/components/openscreen_platform/tls_connection_factory_unittest.cc
@@ -56,7 +56,7 @@
               (override));
   MOCK_METHOD(void,
               OnError,
-              (openscreen::TlsConnectionFactory*, Error),
+              (openscreen::TlsConnectionFactory*, const Error&),
               (override));
 };
 
diff --git a/components/optimization_guide/OWNERS b/components/optimization_guide/OWNERS
index 6839ccc..f4d6cbb 100644
--- a/components/optimization_guide/OWNERS
+++ b/components/optimization_guide/OWNERS
@@ -1,5 +1,6 @@
-sophiechang@chromium.org
+holte@chromium.org
 mcrouse@chromium.org
 rajendrant@chromium.org
 robertogden@chromium.org
+sophiechang@chromium.org
 tbansal@chromium.org
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
index 5c7b15aa..10c26ba 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -763,17 +763,6 @@
     pending_restoration_data_.clear();
 
     session_restore_in_progress = true;
-
-    // During an immersive fullscreen restore the key view loop can become
-    // corrupted. In certain situation this can cause an infinite loop when
-    // looking for the next valid key view, leading to an OOM. Recalculate the
-    // loop after the restore to prevent this. See https://crbug.com/324812653
-    // for details.
-    // TODO(http://crbug.com/40261565): Remove when FB12010731 is fixed in
-    // AppKit.
-    if ([window_ immersiveFullscreen]) {
-      [window_ recalculateKeyViewLoop];
-    }
   }
 
   // Ensure that:
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc
index 02d064b..2f6a209 100644
--- a/components/safe_browsing/core/common/features.cc
+++ b/components/safe_browsing/core/common/features.cc
@@ -133,7 +133,7 @@
 BASE_FEATURE(kHashPrefixRealTimeLookups,
              "SafeBrowsingHashPrefixRealTimeLookups",
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
-    BUILDFLAG(IS_CHROMEOS)
+    BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_IOS)
              base::FEATURE_ENABLED_BY_DEFAULT
 #else
              base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/components/sync_preferences/common_syncable_prefs_database.cc b/components/sync_preferences/common_syncable_prefs_database.cc
index efbd924..4beaf5a 100644
--- a/components/sync_preferences/common_syncable_prefs_database.cc
+++ b/components/sync_preferences/common_syncable_prefs_database.cc
@@ -110,6 +110,7 @@
   kAutofillPaymentCardBenefits = 69,
   // kCloseTabs = 70, (no longer synced)
   kShowTabGroupsInBookmarkBar = 71,
+  kFacilitatedPaymentsPix = 72,
   // See components/sync_preferences/README.md about adding new entries here.
   // vvvvv IMPORTANT! vvvvv
   // Note to the reviewer: IT IS YOUR RESPONSIBILITY to ensure that new syncable
@@ -280,6 +281,11 @@
         {autofill::prefs::kAutofillPaymentCardBenefits,
          {syncable_prefs_ids::kAutofillPaymentCardBenefits, syncer::PREFERENCES,
           PrefSensitivity::kNone, MergeBehavior::kNone}},
+#if BUILDFLAG(IS_ANDROID)
+        {autofill::prefs::kFacilitatedPaymentsPix,
+         {syncable_prefs_ids::kFacilitatedPaymentsPix, syncer::PREFERENCES,
+          PrefSensitivity::kNone, MergeBehavior::kNone}},
+#endif  // BUILDFLAG(IS_ANDROID)
     });
 
 }  // namespace
diff --git a/components/viz/host/host_frame_sink_manager.cc b/components/viz/host/host_frame_sink_manager.cc
index f9648e7..6ccf1a72 100644
--- a/components/viz/host/host_frame_sink_manager.cc
+++ b/components/viz/host/host_frame_sink_manager.cc
@@ -300,6 +300,22 @@
                                            capture_exact_surface_id);
 }
 
+void HostFrameSinkManager::SetOnCopyOutputReadyCallback(
+    const blink::SameDocNavigationScreenshotDestinationToken& destination_token,
+    ScreenshotDestinationReadyCallback callback) {
+  CHECK(screenshot_destinations_.find(destination_token) ==
+        screenshot_destinations_.end());
+  screenshot_destinations_[destination_token] = std::move(callback);
+}
+
+void HostFrameSinkManager::InvalidateCopyOutputReadyCallback(
+    const blink::SameDocNavigationScreenshotDestinationToken&
+        destination_token) {
+  auto it = screenshot_destinations_.find(destination_token);
+  CHECK(it != screenshot_destinations_.end());
+  screenshot_destinations_.erase(it);
+}
+
 void HostFrameSinkManager::Throttle(const std::vector<FrameSinkId>& ids,
                                     base::TimeDelta interval) {
   frame_sink_manager_->Throttle(ids, interval);
@@ -417,6 +433,20 @@
 }
 #endif
 
+void HostFrameSinkManager::OnScreenshotCaptured(
+    const blink::SameDocNavigationScreenshotDestinationToken& destination_token,
+    std::unique_ptr<CopyOutputResult> copy_output_result) {
+  auto it = screenshot_destinations_.find(destination_token);
+  if (it == screenshot_destinations_.end()) {
+    return;
+  }
+  SkBitmap immutable =
+      copy_output_result->ScopedAccessSkBitmap().GetOutScopedBitmap();
+  immutable.setImmutable();
+  std::move(it->second).Run(destination_token, std::move(immutable));
+  screenshot_destinations_.erase(it);
+}
+
 uint32_t HostFrameSinkManager::CacheBackBufferForRootSink(
     const FrameSinkId& root_sink_id) {
   auto it = frame_sink_data_map_.find(root_sink_id);
diff --git a/components/viz/host/host_frame_sink_manager.h b/components/viz/host/host_frame_sink_manager.h
index c3e3b12..a2bf0e2 100644
--- a/components/viz/host/host_frame_sink_manager.h
+++ b/components/viz/host/host_frame_sink_manager.h
@@ -9,6 +9,7 @@
 #include <optional>
 #include <string>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include "base/compiler_specific.h"
@@ -205,6 +206,28 @@
                            std::unique_ptr<CopyOutputRequest> request,
                            bool capture_exact_surface_id = false);
 
+  using ScreenshotDestinationReadyCallback = base::OnceCallback<void(
+      const blink::SameDocNavigationScreenshotDestinationToken&
+          destination_token,
+      SkBitmap copy_output)>;
+  // Sets the callback which is invoked when a `CopyOutputResult` associated
+  // with `destination_token` is received by the host/browser process from the
+  // Viz process. Must be called once per `destination_token`.
+  // This is used to save screenshots for same-document navigations committed in
+  // the renderer process.
+  void SetOnCopyOutputReadyCallback(
+      const blink::SameDocNavigationScreenshotDestinationToken&
+          destination_token,
+      ScreenshotDestinationReadyCallback callback);
+
+  // Invalidates the `ScreenshotDestinationReadyCallback` for
+  // `destination_token`. Used when the destination is no longer eligible for
+  // storing the screenshot (e.g., a later-arrival screenshot after the
+  // destination is destroyed).
+  void InvalidateCopyOutputReadyCallback(
+      const blink::SameDocNavigationScreenshotDestinationToken&
+          destination_token);
+
   void Throttle(const std::vector<FrameSinkId>& ids, base::TimeDelta interval);
   void StartThrottlingAllFrameSinks(base::TimeDelta interval);
   void StopThrottlingAllFrameSinks();
@@ -320,6 +343,10 @@
       const std::vector<int32_t>& thread_ids,
       VerifyThreadIdsDoNotBelongToHostCallback callback) override;
 #endif
+  void OnScreenshotCaptured(
+      const blink::SameDocNavigationScreenshotDestinationToken&
+          destination_token,
+      std::unique_ptr<CopyOutputResult> copy_output_result) override;
 
   // Connections to/from FrameSinkManagerImpl.
   mojo::Remote<mojom::FrameSinkManager> frame_sink_manager_remote_;
@@ -351,6 +378,13 @@
   // This is kept in sync with implementation.
   DebugRendererSettings debug_renderer_settings_;
 
+  // When Viz sends the screenshot back to the host process,
+  // `ScreenshotDestinationReadyCallback` is invoked to stash the screenshot to
+  // the correct destination.
+  base::flat_map<blink::SameDocNavigationScreenshotDestinationToken,
+                 ScreenshotDestinationReadyCallback>
+      screenshot_destinations_;
+
   base::WeakPtrFactory<HostFrameSinkManager> weak_ptr_factory_{this};
 };
 
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 6fe8c2e..1a6fed8 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -509,7 +509,7 @@
     public_deps += [ "//third_party/dawn/include/dawn:headers" ]
 
     deps += [
-      "//third_party/dawn/src/dawn:cpp",
+      "//third_party/dawn/include/dawn:cpp_headers",
       "//third_party/dawn/src/dawn:proc",
       "//third_party/dawn/src/dawn/native",
     ]
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index 261aa36..bf97443 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -94,6 +94,20 @@
   return intersected.value_or(gfx::Rect{});
 }
 
+void RemoveSurfaceReferenceAndDispatchCopyOutputRequestCallback(
+    base::WeakPtr<FrameSinkManagerImpl> frame_sink_manager,
+    SurfaceId copied_surface,
+    const blink::SameDocNavigationScreenshotDestinationToken& destination_token,
+    std::unique_ptr<CopyOutputResult> result) {
+  if (!frame_sink_manager) {
+    return;
+  }
+  frame_sink_manager->surface_manager()->RemoveTemporaryReferenceAfterCopy(
+      copied_surface);
+  frame_sink_manager->OnScreenshotCaptured(destination_token,
+                                           std::move(result));
+}
+
 }  // namespace
 
 CompositorFrameSinkSupport::CompositorFrameSinkSupport(
@@ -890,6 +904,26 @@
 
     current_surface = surface_manager_->CreateSurface(
         weak_factory_.GetWeakPtr(), surface_info);
+
+    // The previous surface needs to be valid to generate a screenshot.
+    if (frame.metadata.screenshot_destination.has_value() && prev_surface) {
+      surface_manager_->AddTemporaryReference(last_created_surface_id_);
+      auto copy_request = std::make_unique<CopyOutputRequest>(
+          CopyOutputRequest::ResultFormat::RGBA,
+          CopyOutputRequest::ResultDestination::kSystemMemory,
+          base::BindOnce(
+              &RemoveSurfaceReferenceAndDispatchCopyOutputRequestCallback,
+              frame_sink_manager_->GetWeakPtr(), last_created_surface_id_,
+              frame.metadata.screenshot_destination.value()));
+      copy_request->set_result_task_runner(
+          base::SequencedTaskRunner::GetCurrentDefault());
+
+      RequestCopyOfOutput(
+          PendingCopyOutputRequest(last_created_surface_id_.local_surface_id(),
+                                   SubtreeCaptureId{}, std::move(copy_request),
+                                   /*capture_exact_id=*/true));
+    }
+
     if (!current_surface) {
       TRACE_EVENT_INSTANT0("viz", "Surface belongs to another client",
                            TRACE_EVENT_SCOPE_THREAD);
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index b89230a3..09462b06 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -113,6 +113,10 @@
       const std::vector<int32_t>& thread_ids,
       VerifyThreadIdsDoNotBelongToHostCallback callback) override {}
 #endif
+  void OnScreenshotCaptured(
+      const blink::SameDocNavigationScreenshotDestinationToken&
+          destination_token,
+      std::unique_ptr<CopyOutputResult> copy_output_result) override {}
 };
 
 class CompositorFrameSinkSupportTest : public testing::Test {
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index fd661e5..9388db5f 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -894,6 +894,13 @@
   navigation_to_animation_manager_.erase(navigation_id);
 }
 
+void FrameSinkManagerImpl::OnScreenshotCaptured(
+    const blink::SameDocNavigationScreenshotDestinationToken& destination_token,
+    std::unique_ptr<CopyOutputResult> copy_output_result) {
+  client_->OnScreenshotCaptured(destination_token,
+                                std::move(copy_output_result));
+}
+
 void FrameSinkManagerImpl::StartFrameCountingForTest(
     base::TimeTicks start_time,
     base::TimeDelta bucket_size) {
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index e2bccd12..aac1418 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -286,6 +286,17 @@
     return frame_counter_ ? &frame_counter_.value() : nullptr;
   }
 
+  // Sends `copy_output_result` tagged by `destination_token` back to
+  // `mojom::FrameSinkManagerClient`.
+  void OnScreenshotCaptured(
+      const blink::SameDocNavigationScreenshotDestinationToken&
+          destination_token,
+      std::unique_ptr<CopyOutputResult> copy_output_result);
+
+  base::WeakPtr<FrameSinkManagerImpl> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
  private:
   friend class FrameSinkManagerTest;
   friend class CompositorFrameSinkSupportTest;
@@ -470,6 +481,8 @@
 
   // Counts frames for test.
   std::optional<FrameCounter> frame_counter_;
+
+  base::WeakPtrFactory<FrameSinkManagerImpl> weak_factory_{this};
 };
 
 }  // namespace viz
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index 713120e..0e545a4 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -85,26 +85,15 @@
 // could be RGBA/BGRA depends on platform and the preference of the buffer
 // format. When user wants ARGB result, it requests a CopyOutputRequest with
 // ResultFormat::RGBA which gives RGBA/BGRA results depends on platform and
-// where the result is stored (system memory or shared texture).
-// In our case, when requesting a kPreferGpuMemoryBuffer, it will create a blit
-// request, results in CopyOutputRequest uses whatever RGBA/BGRA pixel format
-// the GMB is, which we created in advance. For now, it is determined by
-// GetFramePoolPlatformPixelFormat.
+// where the result is stored (buffer format preference).
+// Currently, kPreferGpuMemoryBuffer + ARGB will request BGRA as pixel format,
+// but kDefault + ARGB will be platform dependent because CopyOutputRequest
+// will use kN32_SkColorType (RGBA on Android, BGRA elsewhere) mostly, and use
+// kRGBA_8888_SkColorType on iOS.
 // This is also documented in the mojom comments (https://crrev.com/c/5418235)
 // about SetFormat, indicating the ARGB format may produce RGBA/BGRA frames
 // depends on platform.
 
-media::VideoPixelFormat GetFramePoolPlatformPixelFormat(
-    media::VideoPixelFormat format,
-    mojom::BufferFormatPreference buffer_format_preference) {
-  if (format == media::PIXEL_FORMAT_ARGB &&
-      buffer_format_preference ==
-          mojom::BufferFormatPreference::kPreferGpuMemoryBuffer) {
-    return media::PIXEL_FORMAT_ABGR;
-  }
-  return format;
-}
-
 // Get the frame pool for the specific format. We need context_provider if the
 // format is NV12 or ARGB (when buffer_format_preference is kNativeTexture).
 // Thus, buffer_format_preference is also needed to tell which mode ARGB use.
@@ -124,9 +113,8 @@
       switch (buffer_format_preference) {
         case mojom::BufferFormatPreference::kPreferGpuMemoryBuffer:
           return std::make_unique<GpuMemoryBufferVideoFramePool>(
-              capacity,
-              GetFramePoolPlatformPixelFormat(format, buffer_format_preference),
-              gfx::ColorSpace::CreateSRGB(), context_provider);
+              capacity, format, gfx::ColorSpace::CreateSRGB(),
+              context_provider);
         case mojom::BufferFormatPreference::kDefault:
           return std::make_unique<SharedMemoryVideoFramePool>(capacity);
         default:
@@ -863,10 +851,7 @@
                         region_properties->render_pass_subrect.ToString());
     auto reserve_start_time = base::TimeTicks::Now();
 
-    frame = frame_pool_->ReserveVideoFrame(
-        GetFramePoolPlatformPixelFormat(pixel_format_,
-                                        buffer_format_preference_),
-        capture_size);
+    frame = frame_pool_->ReserveVideoFrame(pixel_format_, capture_size);
 
     UMA_HISTOGRAM_CUSTOM_TIMES(
         "Viz.FrameSinkVideoCapturer.ReserveFrameDuration",
diff --git a/components/viz/service/surfaces/surface_manager.cc b/components/viz/service/surfaces/surface_manager.cc
index a163e1d4..1140031e 100644
--- a/components/viz/service/surfaces/surface_manager.cc
+++ b/components/viz/service/surfaces/surface_manager.cc
@@ -607,6 +607,11 @@
   RemoveTemporaryReferenceImpl(surface_id, RemovedReason::DROPPED);
 }
 
+void SurfaceManager::RemoveTemporaryReferenceAfterCopy(
+    const SurfaceId& surface_id) {
+  RemoveTemporaryReferenceImpl(surface_id, RemovedReason::COPIED);
+}
+
 SurfaceAllocationGroup* SurfaceManager::GetOrCreateAllocationGroupForSurfaceId(
     const SurfaceId& surface_id) {
   std::unique_ptr<SurfaceAllocationGroup>& allocation_group =
diff --git a/components/viz/service/surfaces/surface_manager.h b/components/viz/service/surfaces/surface_manager.h
index ece1d5f8d..19ef7b4c 100644
--- a/components/viz/service/surfaces/surface_manager.h
+++ b/components/viz/service/surfaces/surface_manager.h
@@ -188,6 +188,13 @@
   // next display frame. We will notify SurfaceObservers accordingly.
   void SurfaceWillBeDrawn(Surface* surface);
 
+  // Adds a temporary reference to |surface_id|. The reference will not have an
+  // owner initially.
+  void AddTemporaryReference(const SurfaceId& surface_id);
+
+  // Removes the temporary reference after the `surface_id` is copied.
+  void RemoveTemporaryReferenceAfterCopy(const SurfaceId& surface_id);
+
   // Removes temporary reference to |surface_id| and older surfaces.
   void DropTemporaryReference(const SurfaceId& surface_id);
 
@@ -244,6 +251,7 @@
     DROPPED = 1,   // The surface won't be embedded so it was dropped.
     SKIPPED = 2,   // A newer surface was embedded and the surface was skipped.
     EXPIRED = 4,   // The surface was never embedded and expired.
+    COPIED = 5,    // The surface was copied.
     COUNT
   };
 
@@ -266,10 +274,6 @@
   // Returns whether |surface_id| has a temporary reference or not.
   bool HasTemporaryReference(const SurfaceId& surface_id) const;
 
-  // Adds a temporary reference to |surface_id|. The reference will not have an
-  // owner initially.
-  void AddTemporaryReference(const SurfaceId& surface_id);
-
   // Removes temporary reference to |surface_id| and older surfaces. The
   // |reason| for removing will be recorded with UMA.
   void RemoveTemporaryReferenceImpl(const SurfaceId& surface_id,
@@ -364,8 +368,6 @@
   // Maximum length of uncommitted queue, zero means all frames are committed
   // automatically.
   const size_t max_uncommitted_frames_;
-
-  base::WeakPtrFactory<SurfaceManager> weak_factory_{this};
 };
 
 }  // namespace viz
diff --git a/components/webapps/browser/android/ambient_badge_manager.cc b/components/webapps/browser/android/ambient_badge_manager.cc
index 314f55c..affdb65e 100644
--- a/components/webapps/browser/android/ambient_badge_manager.cc
+++ b/components/webapps/browser/android/ambient_badge_manager.cc
@@ -8,9 +8,7 @@
 #include <optional>
 #include <string>
 
-#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
-#include "components/messages/android/messages_feature.h"
 #include "components/prefs/pref_service.h"
 #include "components/segmentation_platform/public/constants.h"
 #include "components/segmentation_platform/public/input_context.h"
@@ -22,8 +20,6 @@
 #include "components/webapps/browser/android/install_prompt_prefs.h"
 #include "components/webapps/browser/android/shortcut_info.h"
 #include "components/webapps/browser/banners/app_banner_settings_helper.h"
-#include "components/webapps/browser/features.h"
-#include "components/webapps/browser/installable/installable_manager.h"
 #include "components/webapps/browser/installable/ml_installability_promoter.h"
 #include "components/webapps/browser/webapps_client.h"
 #include "content/public/browser/web_contents.h"
@@ -32,8 +28,6 @@
 
 namespace {
 
-constexpr base::TimeDelta kSuppressedForFirsVisitPeriod = base::Days(30);
-
 constexpr char kSegmentationResultHistogramName[] =
     "WebApk.InstallPrompt.SegmentationResult";
 
@@ -46,6 +40,9 @@
   kMaxValue = kShowInstallPrompt,
 };
 
+bool gOverrideSegmentationResultForTesting = false;
+bool gShowInstallPromptForTesting = false;
+
 }  // namespace
 
 AmbientBadgeManager::AmbientBadgeManager(
@@ -76,12 +73,7 @@
   maybe_show_pwa_bottom_sheet_ = std::move(maybe_show_pwa_bottom_sheet);
 
   UpdateState(State::kActive);
-
-  if (base::FeatureList::IsEnabled(features::kInstallPromptSegmentation)) {
-    MaybeShowAmbientBadgeSmart();
-  } else {
-    MaybeShowAmbientBadgeLegacy();
-  }
+  MaybeShowAmbientBadgeSmart();
 }
 
 void AmbientBadgeManager::AddToHomescreenFromBadge() {
@@ -125,79 +117,6 @@
   state_ = state;
 }
 
-void AmbientBadgeManager::MaybeShowAmbientBadgeLegacy() {
-  // Do not show the ambient badge if it was recently dismissed.
-  if (AppBannerSettingsHelper::WasBannerRecentlyBlocked(
-          web_contents(), validated_url_, app_identifier_,
-          AppBannerManager::GetCurrentTime())) {
-    UpdateState(State::kBlocked);
-    return;
-  }
-
-  if (ShouldSuppressAmbientBadgeOnFirstVisit()) {
-    UpdateState(State::kPendingEngagement);
-    return;
-  }
-
-  // if it's showing for web app (not native app), only show if the worker check
-  // already passed.
-  if (a2hs_params_->app_type == AddToHomescreenParams::AppType::WEBAPK &&
-      !passed_worker_check_) {
-    PerformWorkerCheckForAmbientBadge(
-        base::BindOnce(&AmbientBadgeManager::OnWorkerCheckResult,
-                       weak_factory_.GetWeakPtr()));
-    return;
-  }
-
-  ShowAmbientBadge();
-}
-
-bool AmbientBadgeManager::ShouldSuppressAmbientBadgeOnFirstVisit() {
-  if (!base::FeatureList::IsEnabled(
-          features::kAmbientBadgeSuppressFirstVisit)) {
-    return false;
-  }
-
-  std::optional<base::Time> last_could_show_time =
-      AppBannerSettingsHelper::GetSingleBannerEvent(
-          web_contents(), validated_url_, app_identifier_,
-          AppBannerSettingsHelper::APP_BANNER_EVENT_COULD_SHOW_AMBIENT_BADGE);
-
-  AppBannerSettingsHelper::RecordBannerEvent(
-      web_contents(), validated_url_, app_identifier_,
-      AppBannerSettingsHelper::APP_BANNER_EVENT_COULD_SHOW_AMBIENT_BADGE,
-      AppBannerManager::GetCurrentTime());
-
-  if (!last_could_show_time || last_could_show_time->is_null()) {
-    return true;
-  }
-
-  return AppBannerManager::GetCurrentTime() - *last_could_show_time >
-         kSuppressedForFirsVisitPeriod;
-}
-
-void AmbientBadgeManager::PerformWorkerCheckForAmbientBadge(
-    InstallableCallback callback) {
-  UpdateState(State::kPendingWorker);
-  InstallableParams params;
-  params.has_worker = true;
-  params.wait_for_worker = true;
-  InstallableManager* installable_manager =
-      InstallableManager::FromWebContents(&web_contents_.get());
-  installable_manager->GetData(params, std::move(callback));
-}
-
-void AmbientBadgeManager::OnWorkerCheckResult(const InstallableData& data) {
-  if (!data.errors.empty()) {
-    return;
-  }
-  passed_worker_check_ = true;
-
-  if (state_ == State::kPendingWorker) {
-    ShowAmbientBadge();
-  }
-}
-
 void AmbientBadgeManager::MaybeShowAmbientBadgeSmart() {
   if (ShouldMessageBeBlockedByGuardrail()) {
     UpdateState(State::kBlocked);
@@ -211,7 +130,18 @@
   CHECK(validated_url_.is_valid());
   CHECK(a2hs_params_);
 
-  UpdateState(State::kSegmentation);
+  UpdateState(State::kPendingSegmentation);
+
+  if (gOverrideSegmentationResultForTesting) {
+    segmentation_platform::ClassificationResult result(
+        segmentation_platform::PredictionStatus::kSucceeded);
+    result.ordered_labels.emplace_back(
+        gShowInstallPromptForTesting
+            ? MLInstallabilityPromoter::kShowInstallPromptLabel
+            : MLInstallabilityPromoter::kDontShowLabel);
+    OnGotClassificationResult(result);
+    return;
+  }
 
   segmentation_platform::PredictionOptions prediction_options;
   prediction_options.on_demand_execution = true;
@@ -241,9 +171,7 @@
     UMA_HISTOGRAM_ENUMERATION(kSegmentationResultHistogramName,
                               SegmentationResult::kInvalid,
                               SegmentationResult::kMaxValue);
-
-    // If the classification is not ready yet, fallback to the legacy logic.
-    MaybeShowAmbientBadgeLegacy();
+    UpdateState(State::kSegmentationBlock);
     return;
   }
 
@@ -255,9 +183,13 @@
                             show ? SegmentationResult::kShowInstallPrompt
                                  : SegmentationResult::kDontShow,
                             SegmentationResult::kMaxValue);
-  if (show) {
-    ShowAmbientBadge();
+
+  if (!show) {
+    UpdateState(State::kSegmentationBlock);
+    return;
   }
+
+  ShowAmbientBadge();
 }
 
 bool AmbientBadgeManager::ShouldMessageBeBlockedByGuardrail() {
@@ -312,4 +244,10 @@
       a2hs_params_->HasMaskablePrimaryIcon(), url);
 }
 
+// static
+void AmbientBadgeManager::SetOverrideSegmentationResultForTesting(bool show) {
+  gOverrideSegmentationResultForTesting = true;
+  gShowInstallPromptForTesting = show;
+}
+
 }  // namespace webapps
diff --git a/components/webapps/browser/android/ambient_badge_manager.h b/components/webapps/browser/android/ambient_badge_manager.h
index 7e6a3e1a..887c6940d 100644
--- a/components/webapps/browser/android/ambient_badge_manager.h
+++ b/components/webapps/browser/android/ambient_badge_manager.h
@@ -12,9 +12,7 @@
 #include "components/segmentation_platform/public/segmentation_platform_service.h"
 #include "components/webapps/browser/android/installable/installable_ambient_badge_client.h"
 #include "components/webapps/browser/android/installable/installable_ambient_badge_message_controller.h"
-#include "components/webapps/browser/installable/installable_data.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
-#include "components/webapps/browser/installable/installable_params.h"
 #include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
 
@@ -22,14 +20,12 @@
 
 namespace webapps {
 
-class InstallableManager;
 struct AddToHomescreenParams;
-struct InstallableData;
 
 // Coordinates the creation of an install ambient badge, from detecting the
 // eligibility to promote the associated web/native app and creating the ambient
-// badge. Lifecycle: This class is owned by the AppBannerManagerAndroidclass and
-// is instantiated when an ambient badge may be shown.
+// badge. Lifecycle: This class is owned by the AppBannerManagerAndroid class
+// and is instantiated when an ambient badge may be shown.
 class AmbientBadgeManager : public InstallableAmbientBadgeClient {
  public:
   // Returns if the bottom sheet was shown.
@@ -60,10 +56,10 @@
     kBlocked = 2,
 
     // Waiting for service worker install to trigger the banner.
-    kPendingWorker = 3,
+    kPendingWorker = 3,  // Deprecated
 
     // Waiting for sufficient engagement to trigger the ambient badge.
-    kPendingEngagement = 4,
+    kPendingEngagement = 4,  // Deprecated
 
     // Showing Ambient Badge.
     kShowing = 5,
@@ -78,9 +74,12 @@
     kComplete = 8,
 
     // Getting classification result from the segmentation platform.
-    kSegmentation = 9,
+    kPendingSegmentation = 9,
 
-    kMaxValue = kSegmentation,
+    // Blocked by segmentation result.
+    kSegmentationBlock = 10,
+
+    kMaxValue = kSegmentationBlock,
   };
 
   State state() const { return state_; }
@@ -100,9 +99,7 @@
   // Hides the ambient badge if it is showing.
   void HideAmbientBadge();
 
-  // Callback invoked by the InstallableManager once it has finished checking
-  // service worker for showing ambient badge.
-  void OnWorkerCheckResult(const InstallableData& data);
+  static void SetOverrideSegmentationResultForTesting(bool show);
 
  protected:
   virtual void UpdateState(State state);
@@ -116,10 +113,6 @@
   void ShowAmbientBadge();
 
  private:
-  // Perform checks and shows the install ambient badge. Uses legacy conditions
-  // instead of the segmentation APIs.
-  void MaybeShowAmbientBadgeLegacy();
-
   // Uses the segmentation APIs to decide showing the install ambient badge
   void MaybeShowAmbientBadgeSmart();
 
@@ -129,11 +122,6 @@
   // Returns true if the prompt should be block.
   bool ShouldMessageBeBlockedByGuardrail();
 
-  void PerformWorkerCheckForAmbientBadge(InstallableCallback callback);
-
-  // Returns true if it's the first visit and  the badge should be suprressed.
-  bool ShouldSuppressAmbientBadgeOnFirstVisit();
-
   // Message controller for the ambient badge.
   InstallableAmbientBadgeMessageController message_controller_{this};
 
@@ -157,8 +145,6 @@
   // The current ambient badge status.
   State state_ = State::kInactive;
 
-  bool passed_worker_check_ = false;
-
   base::WeakPtrFactory<AmbientBadgeManager> weak_factory_{this};
 };
 
diff --git a/components/webapps/browser/android/app_banner_manager_android.cc b/components/webapps/browser/android/app_banner_manager_android.cc
index 4164f44d..34c998b 100644
--- a/components/webapps/browser/android/app_banner_manager_android.cc
+++ b/components/webapps/browser/android/app_banner_manager_android.cc
@@ -761,4 +761,11 @@
   AppBannerSettingsHelper::SetTotalEngagementToTrigger(engagement);
 }
 
+// static
+void JNI_AppBannerManager_SetOverrideSegmentationResultForTesting(  // IN-TEST
+    JNIEnv* env,
+    jboolean show) {
+  AmbientBadgeManager::SetOverrideSegmentationResultForTesting(show);
+}
+
 }  // namespace webapps
diff --git a/components/webapps/browser/android/java/src/org/chromium/components/webapps/AppBannerManager.java b/components/webapps/browser/android/java/src/org/chromium/components/webapps/AppBannerManager.java
index 2d914db03..9f0fa65b 100644
--- a/components/webapps/browser/android/java/src/org/chromium/components/webapps/AppBannerManager.java
+++ b/components/webapps/browser/android/java/src/org/chromium/components/webapps/AppBannerManager.java
@@ -211,6 +211,11 @@
         AppBannerManagerJni.get().setTotalEngagementToTrigger(engagement);
     }
 
+    /** Sets the install promo result from segmentation service for testing purpose. */
+    public static void setOverrideSegmentationResultForTesting(boolean show) {
+        AppBannerManagerJni.get().setOverrideSegmentationResultForTesting(show);
+    }
+
     /** Returns the AppBannerManager object. This is owned by the C++ banner manager. */
     public static AppBannerManager forWebContents(WebContents contents) {
         ThreadUtils.assertOnUiThread();
@@ -262,5 +267,7 @@
         void setTimeDeltaForTesting(int days);
 
         void setTotalEngagementToTrigger(double engagement);
+
+        void setOverrideSegmentationResultForTesting(boolean show);
     }
 }
diff --git a/components/webapps/browser/features.cc b/components/webapps/browser/features.cc
index 0b3f0eb..b3a91840 100644
--- a/components/webapps/browser/features.cc
+++ b/components/webapps/browser/features.cc
@@ -14,10 +14,6 @@
              "AddToHomescreenMessaging",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kAmbientBadgeSuppressFirstVisit,
-             "AmbientBadgeSuppressFirstVisit",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enables or disables the installable ambient badge message.
 BASE_FEATURE(kInstallPromptGlobalGuardrails,
              "InstallPromptGlobalGuardrails",
@@ -45,11 +41,6 @@
 
 #endif  // BUILDFLAG(IS_ANDROID)
 
-// Use segmentation to decide whether install prompt should be shown.
-BASE_FEATURE(kInstallPromptSegmentation,
-             "InstallPromptSegmentation",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Keys to use when querying the variations params.
 BASE_FEATURE(kAppBannerTriggering,
              "AppBannerTriggering",
diff --git a/components/webapps/browser/features.h b/components/webapps/browser/features.h
index 9d44c556..f97a68a2 100644
--- a/components/webapps/browser/features.h
+++ b/components/webapps/browser/features.h
@@ -28,9 +28,6 @@
 
 #if BUILDFLAG(IS_ANDROID)
 BASE_DECLARE_FEATURE(kAddToHomescreenMessaging);
-BASE_DECLARE_FEATURE(kAmbientBadgeSuppressFirstVisit);
-extern const base::FeatureParam<base::TimeDelta>
-    kAmbientBadgeSuppressFirstVisit_Period;
 BASE_DECLARE_FEATURE(kInstallPromptGlobalGuardrails);
 extern const base::FeatureParam<int>
     kInstallPromptGlobalGuardrails_DismissCount;
@@ -45,8 +42,6 @@
 BASE_DECLARE_FEATURE(kWebApkInstallFailureNotification);
 #endif  // BUILDFLAG(IS_ANDROID)
 
-BASE_DECLARE_FEATURE(kInstallPromptSegmentation);
-
 BASE_DECLARE_FEATURE(kAppBannerTriggering);
 extern const base::FeatureParam<double> kBannerParamsEngagementTotalKey;
 extern const base::FeatureParam<int> kBannerParamsDaysAfterBannerDismissedKey;
diff --git a/components/webapps/browser/installable/ml_installability_promoter.h b/components/webapps/browser/installable/ml_installability_promoter.h
index 2d32226..028a0933 100644
--- a/components/webapps/browser/installable/ml_installability_promoter.h
+++ b/components/webapps/browser/installable/ml_installability_promoter.h
@@ -95,6 +95,7 @@
       public content::WebContentsUserData<MLInstallabilityPromoter> {
  public:
   static constexpr char kShowInstallPromptLabel[] = "ShowInstallPrompt";
+  static constexpr char kDontShowLabel[] = "DontShow";
 
   ~MLInstallabilityPromoter() override;
 
diff --git a/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.mm b/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.mm
index 0dfce563..4da3e57 100644
--- a/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.mm
+++ b/content/app_shim_remote_cocoa/web_contents_ns_view_bridge.mm
@@ -4,7 +4,9 @@
 
 #include "content/app_shim_remote_cocoa/web_contents_ns_view_bridge.h"
 
+#include "base/apple/foundation_util.h"
 #import "base/task/sequenced_task_runner.h"
+#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
 #include "components/remote_cocoa/app_shim/ns_view_ids.h"
 #import "content/app_shim_remote_cocoa/web_contents_view_cocoa.h"
 #include "content/browser/web_contents/web_contents_view_mac.h"
@@ -82,6 +84,29 @@
 }
 
 void WebContentsNSViewBridge::SetVisible(bool visible) {
+  // If the first responder is a child of the current view, AppKit will search
+  // for a new first responder during `-setHidden:`. The key view loop is
+  // searched for a view that can become key. Typically this search yields no
+  // results and the window becomes the default first responder. However if this
+  // occurs after an immersive fullscreen restore an infinite loop can occur
+  // leading to an OOM. This occurs because of the existence of an NSToolbar,
+  // which causes the key loop traversal to jump back and forth between the
+  // view's window and the AppKit owned NSToolbarFullscreenWindow which hosts
+  // the toolbar in immersive fullscreen. To prevent this set the window's first
+  // responder to nil which will make the window the first responder before the
+  // hide.
+  // TODO(http://crbug.com/40261565): Remove when FB12010731 is fixed in
+  // AppKit.
+  NativeWidgetMacNSWindow* widget_window =
+      base::apple::ObjCCast<NativeWidgetMacNSWindow>(ns_view_.window);
+  if (!visible && [widget_window immersiveFullscreen]) {
+    NSView* first_responder =
+        base::apple::ObjCCast<NSView>(ns_view_.window.firstResponder);
+    if ([first_responder isDescendantOf:ns_view_]) {
+      [ns_view_.window makeFirstResponder:nil];
+    }
+  }
+
   [ns_view_ setHidden:!visible];
 }
 
diff --git a/content/browser/android/javascript_injector.cc b/content/browser/android/javascript_injector.cc
index f48ff46..9a6050d 100644
--- a/content/browser/android/javascript_injector.cc
+++ b/content/browser/android/javascript_injector.cc
@@ -50,6 +50,14 @@
     const JavaParamRef<jstring>& name,
     const JavaParamRef<jclass>& safe_annotation_clazz) {
   DCHECK(java_bridge_dispatcher_host_);
+  // If a new js object is added or removed when a page is in BFCache,
+  // the change won't apply after restoring the page.
+  // To avoid this behavior difference when BFCache is involved vs not,
+  // evict all BFCached pages so that we won't
+  // restore any pages that don't have this object modified.
+  // Same for RemoveInterface below.
+  // TODO(crbug.com/331250164): Evict prerendered pages as well
+  GetWebContents().GetController().GetBackForwardCache().Flush();
   java_bridge_dispatcher_host_->AddNamedObject(
       ConvertJavaStringToUTF8(env, name), object, safe_annotation_clazz);
 }
@@ -58,6 +66,7 @@
                                          const JavaParamRef<jobject>& /* obj */,
                                          const JavaParamRef<jstring>& name) {
   DCHECK(java_bridge_dispatcher_host_);
+  GetWebContents().GetController().GetBackForwardCache().Flush();
   java_bridge_dispatcher_host_->RemoveNamedObject(
       ConvertJavaStringToUTF8(env, name));
 }
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index 0f08d81..fe2f96f 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -76,8 +76,7 @@
 #endif
 
 bool RenderFrameHostDelegate::CanEnterFullscreenMode(
-    RenderFrameHostImpl* requesting_frame,
-    const blink::mojom::FullscreenOptions& options) {
+    RenderFrameHostImpl* requesting_frame) {
   return true;
 }
 
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index d2369638..4e766c2 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -318,9 +318,7 @@
 #endif
 
   // Returns whether entering fullscreen with EnterFullscreenMode() is allowed.
-  virtual bool CanEnterFullscreenMode(
-      RenderFrameHostImpl* requesting_frame,
-      const blink::mojom::FullscreenOptions& options);
+  virtual bool CanEnterFullscreenMode(RenderFrameHostImpl* requesting_frame);
 
   // Notification that the frame with the given host wants to enter fullscreen
   // mode. Must only be called if CanEnterFullscreenMode returns true.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 16dc21f..aa31fd1 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -7524,7 +7524,7 @@
     }
   }
 
-  if (!delegate_->CanEnterFullscreenMode(this, *options)) {
+  if (!delegate_->CanEnterFullscreenMode(this)) {
     std::move(callback).Run(/*granted=*/false);
     return;
   }
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 848cef4..6f5660c 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3928,8 +3928,7 @@
 }
 
 bool WebContentsImpl::CanEnterFullscreenMode(
-    RenderFrameHostImpl* requesting_frame,
-    const blink::mojom::FullscreenOptions& options) {
+    RenderFrameHostImpl* requesting_frame) {
   // It's possible that this WebContents was spawned while blocking UI was on
   // the screen, or that it was downstream from a WebContents when UI was
   // blocked. Therefore, disqualify it from fullscreen if it or any upstream
@@ -3938,14 +3937,14 @@
                               [](auto* opener) {
                                 return opener->fullscreen_blocker_count_ == 0;
                               }) &&
-         delegate_->CanEnterFullscreenModeForTab(requesting_frame, options);
+         delegate_->CanEnterFullscreenModeForTab(requesting_frame);
 }
 
 void WebContentsImpl::EnterFullscreenMode(
     RenderFrameHostImpl* requesting_frame,
     const blink::mojom::FullscreenOptions& options) {
   OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode");
-  DCHECK(CanEnterFullscreenMode(requesting_frame, options));
+  DCHECK(CanEnterFullscreenMode(requesting_frame));
   DCHECK(requesting_frame->IsActive());
   DCHECK(ContainsOrIsFocusedWebContents());
   if (base::FeatureList::IsEnabled(
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 4e02164..991ac5d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -704,9 +704,7 @@
   void GetNFC(RenderFrameHost*,
               mojo::PendingReceiver<device::mojom::NFC>) override;
 #endif
-  bool CanEnterFullscreenMode(
-      RenderFrameHostImpl* requesting_frame,
-      const blink::mojom::FullscreenOptions& options) override;
+  bool CanEnterFullscreenMode(RenderFrameHostImpl* requesting_frame) override;
   void EnterFullscreenMode(
       RenderFrameHostImpl* requesting_frame,
       const blink::mojom::FullscreenOptions& options) override;
diff --git a/content/public/browser/prerender_web_contents_delegate.cc b/content/public/browser/prerender_web_contents_delegate.cc
index 83ac089..3a1155b 100644
--- a/content/public/browser/prerender_web_contents_delegate.cc
+++ b/content/public/browser/prerender_web_contents_delegate.cc
@@ -78,8 +78,7 @@
 }
 
 bool PrerenderWebContentsDelegate::CanEnterFullscreenModeForTab(
-    RenderFrameHost* requesting_frame,
-    const blink::mojom::FullscreenOptions& options) {
+    RenderFrameHost* requesting_frame) {
   // This should not be called for a prerendered page.
   NOTREACHED_NORETURN();
 }
diff --git a/content/public/browser/prerender_web_contents_delegate.h b/content/public/browser/prerender_web_contents_delegate.h
index f1b53283b..daaf42f 100644
--- a/content/public/browser/prerender_web_contents_delegate.h
+++ b/content/public/browser/prerender_web_contents_delegate.h
@@ -42,9 +42,7 @@
                           const std::string& frame_name,
                           const GURL& target_url,
                           WebContents* new_contents) override;
-  bool CanEnterFullscreenModeForTab(
-      RenderFrameHost* requesting_frame,
-      const blink::mojom::FullscreenOptions& options) override;
+  bool CanEnterFullscreenModeForTab(RenderFrameHost* requesting_frame) override;
   void EnterFullscreenModeForTab(
       RenderFrameHost* requesting_frame,
       const blink::mojom::FullscreenOptions& options) override;
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 18763ef8..f4d46da 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -188,8 +188,7 @@
 }
 
 bool WebContentsDelegate::CanEnterFullscreenModeForTab(
-    RenderFrameHost* requesting_frame,
-    const blink::mojom::FullscreenOptions& options) {
+    RenderFrameHost* requesting_frame) {
   return true;
 }
 
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 55aa80726..4e003324 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -469,9 +469,7 @@
 
   // Returns whether entering fullscreen with |EnterFullscreenModeForTab()| is
   // allowed.
-  virtual bool CanEnterFullscreenModeForTab(
-      RenderFrameHost* requesting_frame,
-      const blink::mojom::FullscreenOptions& options);
+  virtual bool CanEnterFullscreenModeForTab(RenderFrameHost* requesting_frame);
 
   // Called when the renderer puts a tab into fullscreen mode.
   // |requesting_frame| is the specific content frame requesting fullscreen.
diff --git a/content/test/data/forms/form_controls_browsertest_button_win.png b/content/test/data/forms/form_controls_browsertest_button_win.png
index b425e0f..d86595b7 100644
--- a/content/test/data/forms/form_controls_browsertest_button_win.png
+++ b/content/test/data/forms/form_controls_browsertest_button_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_checkbox_win.png b/content/test/data/forms/form_controls_browsertest_checkbox_win.png
index 27342a78..f4a364c 100644
--- a/content/test/data/forms/form_controls_browsertest_checkbox_win.png
+++ b/content/test/data/forms/form_controls_browsertest_checkbox_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_color_input_win.png b/content/test/data/forms/form_controls_browsertest_color_input_win.png
index d02f2852..57cadb7 100644
--- a/content/test/data/forms/form_controls_browsertest_color_input_win.png
+++ b/content/test/data/forms/form_controls_browsertest_color_input_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_dark_mode_text_selection_win.png b/content/test/data/forms/form_controls_browsertest_dark_mode_text_selection_win.png
index 0193e49..e8985d2d 100644
--- a/content/test/data/forms/form_controls_browsertest_dark_mode_text_selection_win.png
+++ b/content/test/data/forms/form_controls_browsertest_dark_mode_text_selection_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_input_win.png b/content/test/data/forms/form_controls_browsertest_input_win.png
index a58c8f14..2790f738c 100644
--- a/content/test/data/forms/form_controls_browsertest_input_win.png
+++ b/content/test/data/forms/form_controls_browsertest_input_win.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 1b00a8b..73b8845 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/data/forms/form_controls_browsertest_multi_select_win.png b/content/test/data/forms/form_controls_browsertest_multi_select_win.png
index 7e7984a..e867deb8 100644
--- a/content/test/data/forms/form_controls_browsertest_multi_select_win.png
+++ b/content/test/data/forms/form_controls_browsertest_multi_select_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_progress_win.png b/content/test/data/forms/form_controls_browsertest_progress_win.png
index 3197f23..8fb3cd950 100644
--- a/content/test/data/forms/form_controls_browsertest_progress_win.png
+++ b/content/test/data/forms/form_controls_browsertest_progress_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_radio_win.png b/content/test/data/forms/form_controls_browsertest_radio_win.png
index 00576c64..172fbf9 100644
--- a/content/test/data/forms/form_controls_browsertest_radio_win.png
+++ b/content/test/data/forms/form_controls_browsertest_radio_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_range_win.png b/content/test/data/forms/form_controls_browsertest_range_win.png
index 9eb052c..d90da00 100644
--- a/content/test/data/forms/form_controls_browsertest_range_win.png
+++ b/content/test/data/forms/form_controls_browsertest_range_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_select_win.png b/content/test/data/forms/form_controls_browsertest_select_win.png
index 51ca511..225667d4 100644
--- a/content/test/data/forms/form_controls_browsertest_select_win.png
+++ b/content/test/data/forms/form_controls_browsertest_select_win.png
Binary files differ
diff --git a/content/test/data/forms/form_controls_browsertest_textarea_win.png b/content/test/data/forms/form_controls_browsertest_textarea_win.png
index 1940e9d..26083754 100644
--- a/content/test/data/forms/form_controls_browsertest_textarea_win.png
+++ b/content/test/data/forms/form_controls_browsertest_textarea_win.png
Binary files differ
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index b23743d..bada212a 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -1003,6 +1003,22 @@
 crbug.com/332743717 conformance/uniforms/uniform-location.html [ Failure ]
 crbug.com/332743717 [ android android-pixel-4 ] conformance2/canvas/drawingbuffer-storage-test.html [ Failure ]
 crbug.com/332743717 [ linux ] conformance2/canvas/drawingbuffer-storage-test.html [ Failure ]
+crbug.com/332743717 [ mac angle-metal ] conformance2/query/occlusion-query-scissor.html [ Failure ]
+crbug.com/332743717 [ mac intel angle-opengl ] conformance2/canvas/drawingbuffer-storage-test.html [ Failure ]
+crbug.com/332743717 [ chromeos chromeos-board-amd64-generic ] conformance2/canvas/drawingbuffer-storage-test.html [ Failure ]
+crbug.com/332743717 [ android android-pixel-2 ] conformance2/canvas/drawingbuffer-storage-test.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/bufferdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/bufferdata-4gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/buffersubdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/buffersubdata-4gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/getbuffersubdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/getbuffersubdata-4gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/readpixels-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/readpixels-4gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/teximage2d-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/teximage2d-4gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/texsubimage2d-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ android android-pixel-2 ] conformance2/wasm/texsubimage2d-4gb-wasm-memory.html [ Failure ]
 crbug.com/335203259 [ android android-pixel-4 ] conformance2/wasm/bufferdata-16gb-wasm-memory.html [ Failure ]
 crbug.com/335203259 [ android android-pixel-4 ] conformance2/wasm/bufferdata-4gb-wasm-memory.html [ Failure ]
 crbug.com/335203259 [ android android-pixel-4 ] conformance2/wasm/buffersubdata-16gb-wasm-memory.html [ Failure ]
@@ -1039,8 +1055,18 @@
 crbug.com/335203259 [ win target-cpu-32 ] conformance2/wasm/teximage2d-4gb-wasm-memory.html [ Failure ]
 crbug.com/335203259 [ win target-cpu-32 ] conformance2/wasm/texsubimage2d-16gb-wasm-memory.html [ Failure ]
 crbug.com/335203259 [ win target-cpu-32 ] conformance2/wasm/texsubimage2d-4gb-wasm-memory.html [ Failure ]
-crbug.com/332743717 [ mac angle-metal ] conformance2/query/occlusion-query-scissor.html [ Failure ]
-crbug.com/332743717 [ mac intel angle-opengl ] conformance2/canvas/drawingbuffer-storage-test.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-amd64-generic ] conformance2/wasm/bufferdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-amd64-generic ] conformance2/wasm/buffersubdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-amd64-generic ] conformance2/wasm/getbuffersubdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-amd64-generic ] conformance2/wasm/readpixels-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-amd64-generic ] conformance2/wasm/teximage2d-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-amd64-generic ] conformance2/wasm/texsubimage2d-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-jacuzzi ] conformance2/wasm/bufferdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-jacuzzi ] conformance2/wasm/buffersubdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-jacuzzi ] conformance2/wasm/getbuffersubdata-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-jacuzzi ] conformance2/wasm/readpixels-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-jacuzzi ] conformance2/wasm/teximage2d-16gb-wasm-memory.html [ Failure ]
+crbug.com/335203259 [ chromeos chromeos-board-jacuzzi ] conformance2/wasm/texsubimage2d-16gb-wasm-memory.html [ Failure ]
 
 #################################################################
 # Temporary suppressions for introduction of drawingBufferStorage
diff --git a/docs/linux/build_instructions.md b/docs/linux/build_instructions.md
index 5196cf9..c0d17aea 100644
--- a/docs/linux/build_instructions.md
+++ b/docs/linux/build_instructions.md
@@ -12,10 +12,12 @@
 
 ## System requirements
 
-*   A 64-bit Intel machine with at least 8GB of RAM. More than 16GB is highly
-    recommended.
-*   At least 100GB of free disk space.
-*   You must have Git and Python v3.8+ installed already (and `python3` must point
+* A 64-bit Intel machine with at least 8GB of RAM. More than 16GB is highly
+    recommended. If your machine has an SSD, it is recommended to have
+    \>=32GB/>=16GB of swap for machines with 8GB/16GB of RAM respectively.
+* At least 100GB of free disk space. It does not have to be on the same drive;
+ Allocate ~50-80GB on HDD for build.
+* You must have Git and Python v3.8+ installed already (and `python3` must point
     to a Python v3.8+ binary). Depot_tools bundles an appropriate version
     of Python in `$depot_tools/python-bin`, if you don't have an appropriate
     version already on your system.
@@ -24,7 +26,6 @@
 runs 22.04, Jammy Jellyfish). There are some instructions for other distros
 below, but they are mostly unsupported, but installation instructions can be found in [Docker](#docker).
 
-
 ## Install `depot_tools`
 
 Clone the `depot_tools` repository:
@@ -94,7 +95,7 @@
 ```
 
 You may need to adjust the build dependencies for other distros. There are
-some [notes](#notes) at the end of this document, but we make no guarantees
+some [notes](#notes-for-other-distros) at the end of this document, but we make no guarantees
 for their accuracy.
 
 ### Run the hooks
@@ -134,7 +135,7 @@
 * For more info on GN, run `gn help` on the command line or read the
   [quick start guide](https://gn.googlesource.com/gn/+/main/docs/quick_start.md).
 
-### <a name="faster-builds"></a>Faster builds
+### Faster builds
 
 This section contains some things you can change to speed up your builds,
 sorted so that the things that make the biggest difference are first.
@@ -203,7 +204,7 @@
 ]
 ```
 
-and run `gclient sync`. This will regenerate the config files in
+And run `gclient sync`. This will regenerate the config files in
 `buildtools/reclient_cfgs` to use the `rbe_instance` that you just added to your
 `.gclient` file.
 
@@ -290,7 +291,9 @@
 to get to the local physical disk directory where you keep those working
 development directories, consider putting
 
-    alias cd="cd -P"
+```
+alias cd="cd -P"
+```
 
 in your `.bashrc` so that `$PWD` or `cwd` always refers to a physical, not
 logical directory (and make sure `CCACHE_BASEDIR` also refers to a physical
@@ -311,8 +314,9 @@
 required. I.e. mount tmpfs to the output directory where the build output goes:
 
 As root:
-
-    mount -t tmpfs -o size=20G,nr_inodes=40k,mode=1777 tmpfs /path/to/out
+```
+mount -t tmpfs -o size=20G,nr_inodes=40k,mode=1777 tmpfs /path/to/out
+```
 
 *** note
 **Caveat:** You need to have enough RAM + swap to back the tmpfs. For a full
@@ -323,10 +327,10 @@
 Quick and dirty benchmark numbers on a HP Z600 (Intel core i7, 16 cores
 hyperthreaded, 12 GB RAM)
 
-*   With tmpfs:
-    *   12m:20s
-*   Without tmpfs
-    *   15m:40s
+* With tmpfs:
+  * 12m:20s
+* Without tmpfs
+  * 15m:40s
 
 ### Smaller builds
 
@@ -447,12 +451,12 @@
 configuration](https://www.chromium.org/developers/gn-build-configuration) for
 other settings):
 
-*   Build in release mode (debugging symbols require more memory):
+* Build in release mode (debugging symbols require more memory):
     `is_debug = false`
-*   Turn off symbols: `symbol_level = 0`
-*   Build in component mode (this is for development only, it will be slower and
+* Turn off symbols: `symbol_level = 0`
+* Build in component mode (this is for development only, it will be slower and
     may have broken functionality): `is_component_build = true`
-*   For official (ThinLTO) builds on Linux, increase the vm.max_map_count kernel
+* For official (ThinLTO) builds on Linux, increase the vm.max_map_count kernel
     parameter: increase the `vm.max_map_count` value from default (like 65530)
     to for example 262144. You can run the `sudo sysctl -w vm.max_map_count=262144`
     command to set it in the current session from the shell, or add the
@@ -460,13 +464,13 @@
 
 ### More links
 
-*   Information about [building with Clang](../clang.md).
-*   You may want to [use a chroot](using_a_chroot.md) to
+* Information about [building with Clang](../clang.md).
+* You may want to [use a chroot](using_a_chroot.md) to
     isolate yourself from versioning or packaging conflicts.
-*   Cross-compiling for ARM? See [LinuxChromiumArm](chromium_arm.md).
-*   Want to use Eclipse as your IDE? See
+* Cross-compiling for ARM? See [LinuxChromiumArm](chromium_arm.md).
+* Want to use Eclipse as your IDE? See
     [LinuxEclipseDev](eclipse_dev.md).
-*   Want to use your built version as your default browser? See
+* Want to use your built version as your default browser? See
     [LinuxDevBuildAsDefaultBrowser](dev_build_as_default_browser.md).
 
 ## Next Steps
@@ -475,7 +479,7 @@
 Linux, please check out the [Linux Development page](development.md) for
 more information.
 
-## Notes for other distros <a name="notes"></a>
+## Notes for other distros
 
 ### Arch Linux
 
@@ -489,8 +493,8 @@
 
 For the optional packages on Arch Linux:
 
-*   `php-cgi` is provided with `pacman`
-*   `wdiff` is not in the main repository but `dwdiff` is. You can get `wdiff`
+* `php-cgi` is provided with `pacman`
+* `wdiff` is not in the main repository but `dwdiff` is. You can get `wdiff`
     in AUR/`yaourt`
 
 ### Crostini (Debian based)
@@ -666,7 +670,7 @@
 There may be additional Docker-specific issues during compilation. See
 [this bug](https://crbug.com/1377520) for additional details on this.
 
-Note: Clone [depot_tools first](#install-depot_tools).
+Note: [Clone depot_tools](#install-depot_tools) first.
 
 #### Build Steps
 
@@ -697,9 +701,9 @@
 # EXPOSE 8080
 
 RUN useradd -u 1000 chrom-d
-USER chrom-d # Default user, can be root (not advised) or removed
+USER chrom-d # Create normal user with name "chrom-d". Optional and you can use root but not advised.
 
-# Start Chromium Builder "chrom-d"(modify this command as needed)
+# Start Chromium Builder "chrom-d" (modify this command as needed)
 # CMD ["autoninja -C out/Default chrome"]
 CMD ["bash"]
 ```
@@ -717,40 +721,44 @@
 ```shell
 $ docker run --rm \ # close instance upon exit
   -it \ # Run docker interactively
-	--name chrom-b \ # with name "chrom-b"
-	-u root \ # with user root
-	-v /path/on/machine/to/chromium:/chromium \ # With chromium folder mounted
-	-v /path/on/machine/to/depot_tools:/depot_tools \ # With depot_tools mounted
-	chrom-b # Run container with image name "chrom-b"
+  --name chrom-b \ # with name "chrom-b"
+  -u root \ # with user root
+  -v /path/on/machine/to/chromium:/chromium \ # With chromium folder mounted
+  -v /path/on/machine/to/depot_tools:/depot_tools \ # With depot_tools mounted
+  chrom-b # Run container with image name "chrom-b"
 ```
 
-1. Install dependencies:
+4. Install dependencies:
 
 ```shell
 # ./build/install-build-deps.sh # `#` here means run as root which is done in previous step.
 ```
 
 5. Save container image with tag-id name `dpv1.0`. Run this on the machine, not in container
+
 ```shell
 $ docker ps # Get docker running instances, copy the id you get
+# Save/tag running docker container with name "chrom-b" with "dpv1.0"
+# You can choose any tag name you want but propagate name accordingly
+# You will need to create new tags when working on different parts of
+# chromium which requires installing additional dependencies
 $ docker commit <ID from above step> chrom-b:dpv1.0
 # Optional, just saves space by deleting unnecessary images
 $ docker image rmi chrom-b:latest && docker image prune \
   && docker container prune && docker builder prune
 ```
 
-6. [Run hooks](#run-the-hooks): (Optional step, can be done in container as root, normal user or on machine. Here it is done on machine)
-7. Exit container.
+1. [Run hooks](#run-the-hooks). (On docker or machine if you installed depot_tools on machine)
+2. Exit container.
 
 #### Run container
 
 ```shell
 $ docker run --rm \ # close instance upon exit
   -it \ # Run docker interactively
-	--name chrom-b \ # with name "chrom-b"
+  --name chrom-b \ # with name "chrom-b"
   -u $(id -u):$(id -g) \ # Run container as a non-root user with same UID & GID
-	-u root \ # with user root
-	-v /path/on/machine/to/chromium:/chromium \ # With chromium folder mounted
-	-v /path/on/machine/to/depot_tools:/depot_tools \ # With depot_tools mounted
-	chrom-b:dpv1.0 # Run container with image name "chrom-b" and tag dpv1.0
+  -v /path/on/machine/to/chromium:/chromium \ # With chromium folder mounted
+  -v /path/on/machine/to/depot_tools:/depot_tools \ # With depot_tools mounted
+  chrom-b:dpv1.0 # Run container with image name "chrom-b" and tag dpv1.0
 ```
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index a09cc77..a44032d 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -832,6 +832,8 @@
     "service_worker/service_worker_test_utils.h",
     "test_event_router_observer.cc",
     "test_event_router_observer.h",
+    "test_extension_console_observer.cc",
+    "test_extension_console_observer.h",
     "updater/extension_cache_fake.cc",
     "updater/extension_cache_fake.h",
     "updater/extension_downloader_test_helper.cc",
diff --git a/extensions/browser/api/user_scripts/user_scripts_api.cc b/extensions/browser/api/user_scripts/user_scripts_api.cc
index 3a01b30..a1828b4 100644
--- a/extensions/browser/api/user_scripts/user_scripts_api.cc
+++ b/extensions/browser/api/user_scripts/user_scripts_api.cc
@@ -39,6 +39,35 @@
 constexpr char kMatchesMissingError[] =
     "User script with ID '*' must specify 'matches'.";
 
+// Returns true if the given `world_id` is valid from the API perspective.
+// If invalid, populates `error_out`.
+bool IsValidWorldId(const std::optional<std::string>& world_id,
+                    std::string* error_out) {
+  if (!world_id) {
+    // Omitting world ID is valid.
+    return true;
+  }
+
+  if (world_id->empty()) {
+    *error_out = "If specified, `worldId` must be non-empty.";
+    return false;
+  }
+
+  if (world_id->at(0) == '_') {
+    *error_out = "World IDs beginning with '_' are reserved.";
+    return false;
+  }
+
+  static constexpr size_t kMaxWorldIdLength = 256;
+  if (world_id->length() > kMaxWorldIdLength) {
+    *error_out = "World IDs must be at most 256 characters.";
+    return false;
+  }
+
+  // Valid world ID!
+  return true;
+}
+
 api::scripts_internal::SerializedUserScript
 ConvertRegisteredUserScriptToSerializedUserScript(
     api::user_scripts::RegisteredUserScript user_script) {
@@ -120,6 +149,12 @@
     }
   }
 
+  std::string utf8_error;
+  if (!IsValidWorldId(user_script.world_id, &utf8_error)) {
+    *error = base::UTF8ToUTF16(utf8_error);
+    return nullptr;
+  }
+
   // After this, we can just convert to our internal type and rely on our
   // typical parsing to a `UserScript`.
   api::scripts_internal::SerializedUserScript serialized_script =
@@ -558,8 +593,10 @@
     world_id = std::move(params->properties.world_id);
   }
 
-  // TODO(https://crbug.com/331680187): Disallow world IDs that begin with
-  // underscores here and in the script registration flow.
+  std::string error;
+  if (!IsValidWorldId(world_id, &error)) {
+    return RespondNow(Error(std::move(error)));
+  }
 
   // TODO(https://crbug.com/331680187): Add some reasonable limit to the number
   // of worlds an extension may create and configure.
diff --git a/extensions/browser/test_extension_console_observer.cc b/extensions/browser/test_extension_console_observer.cc
new file mode 100644
index 0000000..e2cf12a1a4
--- /dev/null
+++ b/extensions/browser/test_extension_console_observer.cc
@@ -0,0 +1,91 @@
+// 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 "extensions/browser/test_extension_console_observer.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/console_message.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/browser/extension_host.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/process_manager.h"
+#include "extensions/browser/service_worker/service_worker_test_utils.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+
+namespace extensions {
+
+TestExtensionConsoleObserver::TestExtensionConsoleObserver(
+    content::BrowserContext* context,
+    const ExtensionId& extension_id,
+    bool fail_on_error)
+    : extension_id_(extension_id), fail_on_error_(fail_on_error) {
+  int manifest_version = ExtensionRegistry::Get(context)
+                             ->enabled_extensions()
+                             .GetByID(extension_id)
+                             ->manifest_version();
+  if (manifest_version == 3) {
+    scoped_observation_.Observe(
+        service_worker_test_utils::GetServiceWorkerContext(context));
+  } else {
+    CHECK_EQ(manifest_version, 2);
+    ExtensionHost* host =
+        ProcessManager::Get(context)->GetBackgroundHostForExtension(
+            extension_id);
+    WebContentsObserver::Observe(host->host_contents());
+  }
+}
+
+TestExtensionConsoleObserver::~TestExtensionConsoleObserver() = default;
+
+void TestExtensionConsoleObserver::SetAllowedErrorMessages(
+    base::flat_set<std::u16string> allowed_messages) {
+  if (!fail_on_error_) {
+    return;
+  }
+  allowed_errors_ = std::move(allowed_messages);
+}
+
+std::string TestExtensionConsoleObserver::GetMessageAt(size_t index) {
+  if (index < 0 || static_cast<size_t>(index) >= messages_.size()) {
+    ADD_FAILURE() << "Tried to retrieve a non-existent message at index: "
+                  << index;
+    return std::string();
+  }
+  return base::UTF16ToUTF8(messages_[index]);
+}
+
+void TestExtensionConsoleObserver::HandleConsoleMessage(
+    const std::u16string& message,
+    blink::mojom::ConsoleMessageLevel log_level) {
+  if (allowed_errors_.contains(message)) {
+    return;
+  }
+  if (log_level == blink::mojom::ConsoleMessageLevel::kWarning ||
+      log_level == blink::mojom::ConsoleMessageLevel::kError) {
+    messages_.push_back(message);
+  }
+}
+
+void TestExtensionConsoleObserver::OnReportConsoleMessage(
+    int64_t version_id,
+    const GURL& scope,
+    const content::ConsoleMessage& message) {
+  HandleConsoleMessage(message.message, message.message_level);
+}
+
+void TestExtensionConsoleObserver::OnDidAddMessageToConsole(
+    content::RenderFrameHost* source_frame,
+    blink::mojom::ConsoleMessageLevel log_level,
+    const std::u16string& message,
+    int32_t line_no,
+    const std::u16string& source_id,
+    const std::optional<std::u16string>& untrusted_stack_trace) {
+  if (source_frame->GetLastCommittedURL().host_piece() != extension_id_) {
+    return;
+  }
+  HandleConsoleMessage(message, log_level);
+}
+
+}  // namespace extensions
diff --git a/extensions/browser/test_extension_console_observer.h b/extensions/browser/test_extension_console_observer.h
new file mode 100644
index 0000000..002f3dc
--- /dev/null
+++ b/extensions/browser/test_extension_console_observer.h
@@ -0,0 +1,76 @@
+// 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 EXTENSIONS_BROWSER_TEST_EXTENSION_CONSOLE_OBSERVER_H_
+#define EXTENSIONS_BROWSER_TEST_EXTENSION_CONSOLE_OBSERVER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "base/scoped_observation.h"
+#include "content/public/browser/service_worker_context.h"
+#include "content/public/browser/service_worker_context_observer.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "extensions/common/extension_id.h"
+#include "url/gurl.h"
+
+namespace content {
+class BrowserContext;
+struct ConsoleMessage;
+class RenderFrameHost;
+}  // namespace content
+
+namespace extensions {
+
+// Monitors an extension's console for errors or warnings.
+class TestExtensionConsoleObserver
+    : public content::ServiceWorkerContextObserver,
+      public content::WebContentsObserver {
+ public:
+  TestExtensionConsoleObserver(content::BrowserContext* context,
+                               const ExtensionId& extension_id,
+                               bool fail_on_error);
+  ~TestExtensionConsoleObserver() override;
+
+  TestExtensionConsoleObserver(const TestExtensionConsoleObserver&) = delete;
+  TestExtensionConsoleObserver& operator=(const TestExtensionConsoleObserver&) =
+      delete;
+
+  // Add a set of allowed error messages, to ignore.
+  void SetAllowedErrorMessages(base::flat_set<std::u16string> allowed_messages);
+  size_t GetErrorCount() { return messages_.size(); }
+  std::string GetMessageAt(size_t index);
+
+ private:
+  void HandleConsoleMessage(const std::u16string& message,
+                            blink::mojom::ConsoleMessageLevel log_level);
+
+  // ServiceWorkerContextObserver:
+  void OnReportConsoleMessage(int64_t version_id,
+                              const GURL& scope,
+                              const content::ConsoleMessage& message) override;
+
+  // WebContentsObserver:
+  void OnDidAddMessageToConsole(
+      content::RenderFrameHost* source_frame,
+      blink::mojom::ConsoleMessageLevel log_level,
+      const std::u16string& message,
+      int32_t line_no,
+      const std::u16string& source_id,
+      const std::optional<std::u16string>& untrusted_stack_trace) override;
+
+  ExtensionId extension_id_;
+  bool fail_on_error_;
+  base::flat_set<std::u16string> allowed_errors_;
+  std::vector<std::u16string> messages_;
+
+  base::ScopedObservation<content::ServiceWorkerContext,
+                          content::ServiceWorkerContextObserver>
+      scoped_observation_{this};
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_TEST_EXTENSION_CONSOLE_OBSERVER_H_
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 473d01d..d8a267f 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -205,7 +205,7 @@
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/command_buffer/client:webgpu_interface",
     "//gpu/ipc:gpu_thread_holder",
-    "//third_party/dawn/src/dawn:cpp",
+    "//third_party/dawn/include/dawn:cpp_headers",
     "//third_party/dawn/src/dawn:proc",
   ]
   deps = [
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index e14a7ef..5040fb93 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -387,7 +387,7 @@
     "//skia:buildflags",
     "//third_party/angle:angle_image_util",
     "//third_party/angle:angle_version_info",
-    "//third_party/dawn/src/dawn:cpp",
+    "//third_party/dawn/include/dawn:cpp_headers",
     "//third_party/dawn/src/dawn:proc",
     "//third_party/libyuv",
     "//third_party/protobuf:protobuf_lite",
diff --git a/gpu/config/BUILD.gn b/gpu/config/BUILD.gn
index e9833d8..15aaa85b 100644
--- a/gpu/config/BUILD.gn
+++ b/gpu/config/BUILD.gn
@@ -211,7 +211,7 @@
     public_deps += [ "//third_party/dawn/include/dawn:headers" ]
 
     deps += [
-      "//third_party/dawn/src/dawn:cpp",
+      "//third_party/dawn/include/dawn:cpp_headers",
       "//third_party/dawn/src/dawn:proc",
       "//third_party/dawn/src/dawn/native",
     ]
diff --git a/gpu/ipc/service/BUILD.gn b/gpu/ipc/service/BUILD.gn
index a908f37..592487f1 100644
--- a/gpu/ipc/service/BUILD.gn
+++ b/gpu/ipc/service/BUILD.gn
@@ -173,7 +173,7 @@
     ]
   }
   if (skia_use_dawn) {
-    deps += [ "//third_party/dawn/src/dawn:cpp" ]
+    deps += [ "//third_party/dawn/include/dawn:cpp_headers" ]
   }
 }
 
diff --git a/infra/config/generated/testing/gn_isolate_map.pyl b/infra/config/generated/testing/gn_isolate_map.pyl
index e7685cd..77d5e27 100644
--- a/infra/config/generated/testing/gn_isolate_map.pyl
+++ b/infra/config/generated/testing/gn_isolate_map.pyl
@@ -905,6 +905,14 @@
     "label": "//headless:headless_browsertests",
     "type": "console_test_launcher",
   },
+  "headless_shell_wpt": {
+    "label": "//:headless_shell_wpt",
+    "type": "generated_script",
+    "args": [
+      "--results-directory",
+      "${ISOLATED_OUTDIR}",
+    ],
+  },
   "headless_unittests": {
     "label": "//headless:headless_unittests",
     "type": "console_test_launcher",
diff --git a/infra/config/generated/testing/test_suites.pyl b/infra/config/generated/testing/test_suites.pyl
index 725c435..2f2279e 100644
--- a/infra/config/generated/testing/test_suites.pyl
+++ b/infra/config/generated/testing/test_suites.pyl
@@ -1041,7 +1041,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'base_junit_tests': {
         'mixins': [
@@ -1056,7 +1055,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'build_junit_tests': {
         'mixins': [
@@ -1071,7 +1069,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'chrome_java_test_pagecontroller_junit_tests': {
         'mixins': [
@@ -1086,7 +1083,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'chrome_junit_tests': {
         'mixins': [
@@ -1101,7 +1097,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'components_junit_tests': {
         'mixins': [
@@ -1116,7 +1111,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'content_junit_tests': {
         'mixins': [
@@ -1131,7 +1125,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'device_junit_tests': {
         'mixins': [
@@ -1146,7 +1139,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'junit_unit_tests': {
         'mixins': [
@@ -1161,7 +1153,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'keyboard_accessory_junit_tests': {
         'mixins': [
@@ -1176,7 +1167,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'media_base_junit_tests': {
         'mixins': [
@@ -1191,7 +1181,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'module_installer_junit_tests': {
         'mixins': [
@@ -1206,7 +1195,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'net_junit_tests': {
         'mixins': [
@@ -1221,7 +1209,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'paint_preview_junit_tests': {
         'mixins': [
@@ -1236,7 +1223,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'password_check_junit_tests': {
         'mixins': [
@@ -1251,7 +1237,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'password_manager_junit_tests': {
         'mixins': [
@@ -1266,7 +1251,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'services_junit_tests': {
         'mixins': [
@@ -1281,7 +1265,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'touch_to_fill_junit_tests': {
         'mixins': [
@@ -1296,7 +1279,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'ui_junit_tests': {
         'mixins': [
@@ -1311,7 +1293,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'webapk_client_junit_tests': {
         'mixins': [
@@ -1326,7 +1307,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'webapk_shell_apk_h2o_junit_tests': {
         'mixins': [
@@ -1341,7 +1321,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'webapk_shell_apk_junit_tests': {
         'mixins': [
@@ -1356,7 +1335,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
     },
 
@@ -1649,33 +1627,6 @@
       },
     },
 
-    'chromium_wpt_tests_old_headless_isolated_scripts': {
-      'chrome_wpt_tests_old_headless': {
-        'test': 'chrome_wpt_tests',
-        'results_handler': 'layout tests',
-        'mixins': [
-          'has_native_resultdb_integration',
-        ],
-        'args': [
-          '--test-type',
-          'testharness',
-          'reftest',
-          'crashtest',
-          'print-reftest',
-          '--additional-driver-flag=--headless=old',
-        ],
-        'swarming': {
-          'shards': 1,
-        },
-        'merge': {
-          'script': '//third_party/blink/tools/merge_web_test_results.py',
-          'args': [
-            '--verbose',
-          ],
-        },
-      },
-    },
-
     'clang_tot_gtests': {
       'base_unittests': {},
     },
@@ -4118,6 +4069,25 @@
       'headless_unittests': {},
     },
 
+    'headless_shell_wpt_tests_isolated_scripts': {
+      'headless_shell_wpt_tests': {
+        'test': 'headless_shell_wpt',
+        'results_handler': 'layout tests',
+        'mixins': [
+          'has_native_resultdb_integration',
+        ],
+        'swarming': {
+          'shards': 10,
+        },
+        'merge': {
+          'script': '//third_party/blink/tools/merge_web_test_results.py',
+          'args': [
+            '--verbose',
+          ],
+        },
+      },
+    },
+
     'ios_blink_tests': {
       'absl_hardening_tests': {},
       'angle_unittests': {
@@ -6042,7 +6012,7 @@
     'chrome_wpt_tests_three_modes': [
       'chromium_wpt_tests_headful_isolated_scripts',
       'chromium_wpt_tests_isolated_scripts',
-      'chromium_wpt_tests_old_headless_isolated_scripts',
+      'headless_shell_wpt_tests_isolated_scripts',
     ],
 
     'chromeos_device_no_gtests': [
diff --git a/infra/config/lib/targets.star b/infra/config/lib/targets.star
index e6d3bdf..933812e 100644
--- a/infra/config/lib/targets.star
+++ b/infra/config/lib/targets.star
@@ -903,7 +903,7 @@
                     spec, error = _apply_mixin(spec, m.props.mixin_values)
                     if error:
                         fail("modifying {} {} with {} failed: {}"
-                            .format(spec.handler.type_name, test.key.id, mixin, error))
+                            .format(spec.handler.type_name, test.key.id, m, error))
                 test_spec_and_source_by_name[test.key.id] = spec, n.key
 
             for child in graph.children(n.key, kind = _targets_nodes.BUNDLE.kind):
diff --git a/infra/config/targets/basic_suites.star b/infra/config/targets/basic_suites.star
index 50b5b6143..d781007 100644
--- a/infra/config/targets/basic_suites.star
+++ b/infra/config/targets/basic_suites.star
@@ -1117,7 +1117,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "base_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1132,7 +1131,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "build_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1147,7 +1145,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "chrome_java_test_pagecontroller_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1162,7 +1159,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "chrome_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1177,7 +1173,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "components_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1192,7 +1187,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "content_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1207,7 +1201,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "device_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1222,7 +1215,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "junit_unit_tests": targets.legacy_test_config(
             mixins = [
@@ -1237,7 +1229,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "keyboard_accessory_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1252,7 +1243,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "media_base_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1267,7 +1257,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "module_installer_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1282,7 +1271,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "net_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1297,7 +1285,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "paint_preview_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1312,7 +1299,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "password_check_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1327,7 +1313,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "password_manager_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1342,7 +1327,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "services_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1357,7 +1341,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "touch_to_fill_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1372,7 +1355,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "ui_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1387,7 +1369,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "webapk_client_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1402,7 +1383,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "webapk_shell_apk_h2o_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1417,7 +1397,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
         "webapk_shell_apk_junit_tests": targets.legacy_test_config(
             mixins = [
@@ -1432,7 +1411,6 @@
                 "walleye",
                 "pie_fleet",
             ],
-            use_isolated_scripts_api = True,
         ),
     },
 )
@@ -1591,19 +1569,11 @@
 )
 
 targets.legacy_basic_suite(
-    name = "chromium_wpt_tests_old_headless_isolated_scripts",
+    name = "headless_shell_wpt_tests_isolated_scripts",
     tests = {
-        "chrome_wpt_tests_old_headless": targets.legacy_test_config(
-            args = [
-                "--test-type",
-                "testharness",
-                "reftest",
-                "crashtest",
-                "print-reftest",
-                "--additional-driver-flag=--headless=old",
-            ],
+        "headless_shell_wpt_tests": targets.legacy_test_config(
             swarming = targets.swarming(
-                shards = 1,
+                shards = 10,
             ),
         ),
     },
diff --git a/infra/config/targets/binaries.star b/infra/config/targets/binaries.star
index 41a6fdb1..77f31ec 100644
--- a/infra/config/targets/binaries.star
+++ b/infra/config/targets/binaries.star
@@ -832,6 +832,22 @@
     label = "//media/gpu/vaapi/test/fake_libva_driver:fake_libva_driver_unittest",
 )
 
+targets.binaries.generated_script(
+    name = "headless_shell_wpt",
+    label = "//:headless_shell_wpt",
+    results_handler = "layout tests",
+    args = [
+        "--results-directory",
+        "${ISOLATED_OUTDIR}",
+    ],
+    merge = targets.merge(
+        script = "//third_party/blink/tools/merge_web_test_results.py",
+        args = [
+            "--verbose",
+        ],
+    ),
+)
+
 targets.binaries.console_test_launcher(
     name = "video_decode_accelerator_tests",
     label = "//media/gpu/test:video_decode_accelerator_tests",
diff --git a/infra/config/targets/compound_suites.star b/infra/config/targets/compound_suites.star
index f7148ddc..b43dd6c3 100644
--- a/infra/config/targets/compound_suites.star
+++ b/infra/config/targets/compound_suites.star
@@ -171,7 +171,7 @@
     basic_suites = [
         "chromium_wpt_tests_isolated_scripts",
         "chromium_wpt_tests_headful_isolated_scripts",
-        "chromium_wpt_tests_old_headless_isolated_scripts",
+        "headless_shell_wpt_tests_isolated_scripts",
     ],
 )
 
diff --git a/infra/config/targets/tests.star b/infra/config/targets/tests.star
index 9ebe628..3d89a2a52 100644
--- a/infra/config/targets/tests.star
+++ b/infra/config/targets/tests.star
@@ -568,11 +568,11 @@
 )
 
 targets.tests.isolated_script_test(
-    name = "chrome_wpt_tests_old_headless",
+    name = "headless_shell_wpt_tests",
     mixins = [
         "has_native_resultdb_integration",
     ],
-    binary = "chrome_wpt_tests",
+    binary = "headless_shell_wpt",
 )
 
 targets.tests.gtest_test(
diff --git a/internal b/internal
index df97b0f..5178496 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit df97b0f7451528e858398030e718d637a29aaa5f
+Subproject commit 51784961a549aa3eff970c32af08734422989955
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
index c7e97346..608039c 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
@@ -391,7 +391,9 @@
 
 // Creates and sets up the view hierarchy.
 - (void)createViewHierarchy {
-  self.layoutGuide = AddLayoutGuideToContentView(self.contentView);
+  self.layoutGuide = AddLayoutGuideToContentView(
+      self.contentView,
+      /*cell_has_header=*/!IsKeyboardAccessoryUpgradeEnabled());
 
   self.selectionStyle = UITableViewCellSelectionStyleNone;
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.mm
index bb6ed44e..ad757cac 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.mm
@@ -182,7 +182,8 @@
 
 // Creates and sets up the view hierarchy.
 - (void)createViewHierarchy {
-  self.layoutGuide = AddLayoutGuideToContentView(self.contentView);
+  self.layoutGuide =
+      AddLayoutGuideToContentView(self.contentView, /*cell_has_header=*/YES);
 
   self.selectionStyle = UITableViewCellSelectionStyleNone;
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h
index eff84f49..387c265 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h
@@ -38,6 +38,8 @@
     kLabeledChipButton,
     // A chip button that is not the first of its group and is unlabeled.
     kOtherChipButton,
+    // A grey line to separate some parts of the cell.
+    kSeparator,
     // Any other element not falling into one of the above types.
     kOther,
   };
@@ -128,6 +130,10 @@
 NSMutableAttributedString* CreateHeaderAttributedString(NSString* title,
                                                         NSString* subtitle);
 
+// Creates an horizontal stack view containing an icon and a label. Used to
+// create the different manual fill cells' header.
+UIStackView* CreateHeaderView(UIView* icon, UILabel* label);
+
 // Creates a gray horizontal line separator, with the same margin as the other
 // components here. The gray line is added to the given `container` and proper
 // constraints are enabled to keep the line at the bottom of the container and
@@ -135,6 +141,9 @@
 UIView* CreateGraySeparatorForContainer(UIView* container);
 
 // Creates a layout guide for the cell and adds it to the given 'content_view`.
-UILayoutGuide* AddLayoutGuideToContentView(UIView* content_view);
+// `cell_has_header` indicates whether or not the layout guide should take a
+// header into account.
+UILayoutGuide* AddLayoutGuideToContentView(UIView* content_view,
+                                           BOOL cell_has_header);
 
 #endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_MANUAL_FILL_CELL_UTILS_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.mm
index 8b7dc68..e09a155 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.mm
@@ -24,10 +24,16 @@
 // Line spacing for the cell's header title.
 constexpr CGFloat kHeaderAttributedStringLineSpacing = 2;
 
+// Minimum height for the header view.
+constexpr CGFloat kHeaderViewMinHeight = 44;
+
 // Horizontal spacing between views used in
 // `AppendHorizontalConstraintsForViews`.
 constexpr CGFloat kHorizontalSpacing = 16;
 
+// Height of the grey separator.
+constexpr CGFloat kSeparatorHeight = 1;
+
 // Vertical spacing between views. Used when the Keyboard Accessory Upgrade
 // feature is disabled.
 constexpr CGFloat kVerticalSpacing = 8;
@@ -36,8 +42,9 @@
 // cell and the chip groups.
 constexpr CGFloat kGenericVerticalSpacingBetweenViews = 16;
 
-// Vertical spacing between two chip buttons.
-constexpr CGFloat kVerticalSpacingBetweenChips = 4;
+// Small vertical spacing between views. Used to visually group chips together
+// and as vertical padding for the cell's header.
+constexpr CGFloat kSmallVerticalSpacingBetweenViews = 4;
 
 // Vertical spacing between two labeled chip buttons.
 constexpr CGFloat kVerticalSpacingBetweenLabeledChips = 8;
@@ -56,8 +63,9 @@
       return kGenericVerticalSpacingBetweenViews;
     case ManualFillCellView::ElementType::kLabeledChipButton:
       return kVerticalSpacingBetweenLabeledChips;
+    case ManualFillCellView::ElementType::kSeparator:
     case ManualFillCellView::ElementType::kOtherChipButton:
-      return kVerticalSpacingBetweenChips;
+      return kSmallVerticalSpacingBetweenViews;
   }
 }
 
@@ -285,37 +293,70 @@
   return attributed_title;
 }
 
-UIView* CreateGraySeparatorForContainer(UIView* container) {
-  UIView* grayLine = [[UIView alloc] init];
-  grayLine.backgroundColor = [UIColor colorNamed:kSeparatorColor];
-  grayLine.translatesAutoresizingMaskIntoConstraints = NO;
-  [container addSubview:grayLine];
+UIStackView* CreateHeaderView(UIView* icon, UILabel* label) {
+  UIStackView* header_view =
+      [[UIStackView alloc] initWithArrangedSubviews:@[ icon, label ]];
+  header_view.translatesAutoresizingMaskIntoConstraints = NO;
+  header_view.spacing = UIStackViewSpacingUseSystem;  // Spacing of 8px.
+  header_view.alignment = UIStackViewAlignmentCenter;
 
-  id<LayoutGuideProvider> safeArea = container.safeAreaLayoutGuide;
-  [NSLayoutConstraint activateConstraints:@[
-    // Vertical constraints.
-    [grayLine.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
-    [grayLine.heightAnchor constraintEqualToConstant:1],
-    // Horizontal constraints.
-    [grayLine.leadingAnchor constraintEqualToAnchor:safeArea.leadingAnchor
-                                           constant:kCellMargin],
-    [safeArea.trailingAnchor constraintEqualToAnchor:grayLine.trailingAnchor
-                                            constant:kCellMargin],
-  ]];
+  if (IsKeyboardAccessoryUpgradeEnabled()) {
+    [NSLayoutConstraint activateConstraints:@[
+      [header_view.heightAnchor
+          constraintGreaterThanOrEqualToConstant:kHeaderViewMinHeight],
+    ]];
+  }
 
-  return grayLine;
+  return header_view;
 }
 
-UILayoutGuide* AddLayoutGuideToContentView(UIView* content_view) {
+UIView* CreateGraySeparatorForContainer(UIView* container) {
+  UIView* gray_line = [[UIView alloc] init];
+  gray_line.backgroundColor = [UIColor colorNamed:kSeparatorColor];
+  gray_line.translatesAutoresizingMaskIntoConstraints = NO;
+  [container addSubview:gray_line];
+
+  id<LayoutGuideProvider> safe_area = container.safeAreaLayoutGuide;
+  if (IsKeyboardAccessoryUpgradeEnabled()) {
+    [NSLayoutConstraint activateConstraints:@[
+      // Vertical constraints.
+      [gray_line.heightAnchor constraintEqualToConstant:kSeparatorHeight],
+      // Horizontal constraints.
+      [gray_line.leadingAnchor constraintEqualToAnchor:safe_area.leadingAnchor
+                                              constant:kCellMargin],
+      [safe_area.trailingAnchor
+          constraintEqualToAnchor:gray_line.trailingAnchor],
+    ]];
+  } else {
+    [NSLayoutConstraint activateConstraints:@[
+      // Vertical constraints.
+      [gray_line.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
+      [gray_line.heightAnchor constraintEqualToConstant:kSeparatorHeight],
+      // Horizontal constraints.
+      [gray_line.leadingAnchor constraintEqualToAnchor:safe_area.leadingAnchor
+                                              constant:kCellMargin],
+      [safe_area.trailingAnchor constraintEqualToAnchor:gray_line.trailingAnchor
+                                               constant:kCellMargin],
+    ]];
+  }
+
+  return gray_line;
+}
+
+UILayoutGuide* AddLayoutGuideToContentView(UIView* content_view,
+                                           BOOL cell_has_header) {
   UILayoutGuide* layout_guide = [[UILayoutGuide alloc] init];
   [content_view addLayoutGuide:layout_guide];
 
   id<LayoutGuideProvider> safe_area = content_view.safeAreaLayoutGuide;
+  CGFloat top_margin = cell_has_header && IsKeyboardAccessoryUpgradeEnabled()
+                           ? kSmallVerticalSpacingBetweenViews
+                           : kCellMargin;
   CGFloat bottom_margin =
       IsKeyboardAccessoryUpgradeEnabled() ? kCellMargin : kCellBottomMargin;
   [NSLayoutConstraint activateConstraints:@[
     [layout_guide.topAnchor constraintEqualToAnchor:content_view.topAnchor
-                                           constant:kCellMargin],
+                                           constant:top_margin],
     [layout_guide.bottomAnchor constraintEqualToAnchor:content_view.bottomAnchor
                                               constant:-bottom_margin],
     [layout_guide.leadingAnchor constraintEqualToAnchor:safe_area.leadingAnchor
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.mm
index b5b1ac8a..b1ff627 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.mm
@@ -114,6 +114,9 @@
 // The constraints for the visible favicon.
 @property(nonatomic, strong) NSArray<NSLayoutConstraint*>* faviconContraints;
 
+// The view displayed at the top the cell containing the favicon and site name.
+@property(nonatomic, strong) UIView* headerView;
+
 // The favicon for the credential. Of type FaviconView when the Keyboard
 // Accessory Upgrade is disabled, and FaviconContainerView when enabled.
 @property(nonatomic, strong) UIView* faviconView;
@@ -191,11 +194,16 @@
     if (IsKeyboardAccessoryUpgradeEnabled()) {
       self.siteNameLabel.numberOfLines = 0;
     }
-    AddViewToVerticalLeadViews(self.siteNameLabel,
-                               ManualFillCellView::ElementType::kOther,
-                               verticalLeadViews);
     self.siteNameLabel.hidden = NO;
     self.faviconView.hidden = NO;
+    AddViewToVerticalLeadViews(self.headerView,
+                               ManualFillCellView::ElementType::kOther,
+                               verticalLeadViews);
+    if (IsKeyboardAccessoryUpgradeEnabled()) {
+      AddViewToVerticalLeadViews(self.grayLine,
+                                 ManualFillCellView::ElementType::kSeparator,
+                                 verticalLeadViews);
+    }
   }
 
   // Holds the chip buttons related to the credential that are vertical leads.
@@ -262,7 +270,8 @@
 
 // Creates and sets up the view hierarchy.
 - (void)createViewHierarchy {
-  self.layoutGuide = AddLayoutGuideToContentView(self.contentView);
+  self.layoutGuide =
+      AddLayoutGuideToContentView(self.contentView, /*cell_has_header=*/YES);
 
   self.selectionStyle = UITableViewCellSelectionStyleNone;
 
@@ -283,23 +292,13 @@
   ];
 
   self.siteNameLabel = CreateLabel();
-  self.siteNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
-  self.siteNameLabel.adjustsFontForContentSizeCategory = YES;
-  [self.contentView addSubview:self.siteNameLabel];
-
-  UIStackView* stackView = [[UIStackView alloc]
-      initWithArrangedSubviews:@[ self.faviconView, self.siteNameLabel ]];
-  stackView.translatesAutoresizingMaskIntoConstraints = NO;
-  stackView.spacing = UIStackViewSpacingUseSystem;
-  stackView.alignment = UIStackViewAlignmentCenter;
-
-  [self.contentView addSubview:stackView];
+  self.headerView = CreateHeaderView(self.faviconView, self.siteNameLabel);
+  [self.contentView addSubview:self.headerView];
+  AppendHorizontalConstraintsForViews(staticConstraints, @[ self.headerView ],
+                                      self.layoutGuide);
 
   self.grayLine = CreateGraySeparatorForContainer(self.contentView);
 
-  AppendHorizontalConstraintsForViews(staticConstraints, @[ stackView ],
-                                      self.layoutGuide);
-
   self.usernameButton = CreateChipWithSelectorAndTarget(
       @selector(userDidTapUsernameButton:), self);
   [self.contentView addSubview:self.usernameButton];
diff --git a/ios/components/credential_provider_extension/password_spec_fetcher.mm b/ios/components/credential_provider_extension/password_spec_fetcher.mm
index 5c4979bd..f7d3eef 100644
--- a/ios/components/credential_provider_extension/password_spec_fetcher.mm
+++ b/ios/components/credential_provider_extension/password_spec_fetcher.mm
@@ -4,6 +4,8 @@
 
 #import "ios/components/credential_provider_extension/password_spec_fetcher.h"
 
+#import <string_view>
+
 #import "base/base64.h"
 #import "components/autofill/core/browser/proto/password_requirements.pb.h"
 
@@ -110,8 +112,8 @@
 
   // Parse the proto and execute completion.
   std::string decoded;
-  const base::StringPiece encoded_bytes(static_cast<const char*>([data bytes]),
-                                        [data length]);
+  const std::string_view encoded_bytes(static_cast<const char*>([data bytes]),
+                                       [data length]);
   if (base::Base64Decode(encoded_bytes, &decoded)) {
     DomainSuggestions suggestions;
     suggestions.ParseFromString(decoded);
diff --git a/ios/third_party/earl_grey2/src b/ios/third_party/earl_grey2/src
index a09fa0c..26acfe3 160000
--- a/ios/third_party/earl_grey2/src
+++ b/ios/third_party/earl_grey2/src
@@ -1 +1 @@
-Subproject commit a09fa0c5cf18578d1985f3abffb599ecdc6fd514
+Subproject commit 26acfe36928095395dfcb6ff3d0578550bd868ac
diff --git a/ios/web/content/BUILD.gn b/ios/web/content/BUILD.gn
index 6c22d4b7c..671d08f 100644
--- a/ios/web/content/BUILD.gn
+++ b/ios/web/content/BUILD.gn
@@ -40,6 +40,7 @@
     ":send_webkit_message_js",
     "//base",
     "//build:blink_buildflags",
+    "//components/embedder_support/ios:web_contents_delegate",
     "//components/js_injection/browser",
     "//content/public/browser",
     "//ios/web/annotations",
diff --git a/ios/web/content/DEPS b/ios/web/content/DEPS
index 0c9a24c..bc8eceb 100644
--- a/ios/web/content/DEPS
+++ b/ios/web/content/DEPS
@@ -5,6 +5,7 @@
 ]
 specific_include_rules = {
   "content_web_state\.mm": [
+    "+components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.h",
     "+third_party/blink/public/mojom/favicon/favicon_url.mojom.h",
     "+skia/ext/skia_utils_ios.h",
   ],
diff --git a/ios/web/content/web_state/content_web_state.h b/ios/web/content/web_state/content_web_state.h
index 340aa9d..c6156455 100644
--- a/ios/web/content/web_state/content_web_state.h
+++ b/ios/web/content/web_state/content_web_state.h
@@ -200,6 +200,11 @@
   int GetVirtualKeyboardHeight(content::WebContents* web_contents) override;
   bool OnlyExpandTopControlsAtPageTop() override;
   void SetTopControlsGestureScrollInProgress(bool in_progress) override;
+  std::unique_ptr<content::ColorChooser> OpenColorChooser(
+      content::WebContents* web_contents,
+      SkColor color,
+      const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions)
+      override;
 
  private:
   // Helper method to register notification observers.
diff --git a/ios/web/content/web_state/content_web_state.mm b/ios/web/content/web_state/content_web_state.mm
index a49e97d4..0558d77 100644
--- a/ios/web/content/web_state/content_web_state.mm
+++ b/ios/web/content/web_state/content_web_state.mm
@@ -6,6 +6,7 @@
 
 #import "base/apple/foundation_util.h"
 #import "base/strings/utf_string_conversions.h"
+#import "components/embedder_support/ios/delegate/color_chooser/color_chooser_ios.h"
 #import "content/public/browser/navigation_entry.h"
 #import "content/public/browser/web_contents.h"
 #import "ios/web/content/content_browser_context.h"
@@ -740,4 +741,12 @@
   keyboard_height_ = 0;
 }
 
+std::unique_ptr<content::ColorChooser> ContentWebState::OpenColorChooser(
+    content::WebContents* web_contents,
+    SkColor color,
+    const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions) {
+  return std::make_unique<web_contents_delegate_ios::ColorChooserIOS>(
+      web_contents, color, suggestions);
+}
+
 }  // namespace web
diff --git a/ios_internal b/ios_internal
index 8af50d3..4e9a9e1 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 8af50d3b84698254f56ba6ffd69171a2abce3e12
+Subproject commit 4e9a9e1ff027107175ef685b140354acc5bfc1a9
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index be069b4..25a62934 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -67,6 +67,7 @@
       "//media/gpu:buildflags",
       "//media/gpu:common",
       "//testing/gtest",
+      "//ui/gl:test_support",
     ]
   }
 
diff --git a/media/gpu/test/video_encode_accelerator_tests.cc b/media/gpu/test/video_encode_accelerator_tests.cc
index b74949e..0116a97 100644
--- a/media/gpu/test/video_encode_accelerator_tests.cc
+++ b/media/gpu/test/video_encode_accelerator_tests.cc
@@ -26,11 +26,14 @@
 #include "media/gpu/test/video_encoder/video_encoder_client.h"
 #include "media/gpu/test/video_encoder/video_encoder_test_environment.h"
 #include "media/gpu/test/video_frame_file_writer.h"
-#include "media/gpu/test/video_frame_helpers.h"
 #include "media/gpu/test/video_frame_validator.h"
 #include "media/gpu/test/video_test_environment.h"
 #include "media/gpu/test/video_test_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/init/gl_factory.h"
+#include "ui/gl/test/gl_surface_test_support.h"
+#include "ui/gl/test/gl_test_support.h"
 
 namespace media {
 namespace test {
@@ -966,5 +969,9 @@
   media::test::g_env = static_cast<media::test::VideoEncoderTestEnvironment*>(
       testing::AddGlobalTestEnvironment(test_environment));
 
+  raw_ptr<gl::GLDisplay> display =
+      gl::GLTestSupport::InitializeGL(std::nullopt);
+  gl::init::CreateOffscreenGLSurface(display, gfx::Size());
+
   return RUN_ALL_TESTS();
 }
diff --git a/services/on_device_model/BUILD.gn b/services/on_device_model/BUILD.gn
index b6da248..63280d2 100644
--- a/services/on_device_model/BUILD.gn
+++ b/services/on_device_model/BUILD.gn
@@ -56,7 +56,7 @@
     if (is_linux || is_chromeos) {
       deps += [
         "//gpu/config",
-        "//third_party/dawn/src/dawn:cpp",
+        "//third_party/dawn/include/dawn:cpp_headers",
         "//third_party/dawn/src/dawn:proc",
         "//third_party/dawn/src/dawn/native",
       ]
diff --git a/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom b/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom
index b8792901..52dc686 100644
--- a/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom
+++ b/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom
@@ -20,7 +20,9 @@
 import "services/viz/public/mojom/compositing/surface_id.mojom";
 import "services/viz/public/mojom/compositing/surface_info.mojom";
 import "services/viz/public/mojom/hit_test/aggregated_hit_test_region.mojom";
+import "services/viz/public/mojom/compositing/copy_output_result.mojom";
 import "services/viz/public/mojom/compositing/video_detector_observer.mojom";
+import "third_party/blink/public/mojom/tokens/tokens.mojom";
 
 // Initialization parameters for a RootCompositorFrameSink.
 struct RootCompositorFrameSinkParams {
@@ -246,4 +248,15 @@
   [EnableIf=is_android]
   VerifyThreadIdsDoNotBelongToHost(array<int32> thread_ids)
       => (bool passed_verification);
+
+  // Allows the GPU process to send the result of a screenshot back to the host
+  // process. The host stores the screenshot to the destination specified by
+  // `destination_token`.
+  //
+  // This is used to copy a screenshot after same-document navigation committed
+  // in the renderer process.
+  OnScreenshotCaptured(
+    blink.mojom.SameDocNavigationScreenshotDestinationToken destination_token,
+    CopyOutputResult copy_output_result
+  );
 };
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 16d475dc..ee2049b 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -840,7 +840,6 @@
       public += skia_graphite_dawn_public
       public_deps = [
         "//third_party/dawn/include/dawn:cpp_headers",
-        "//third_party/dawn/src/dawn:cpp",
         "//third_party/dawn/src/dawn:proc",
       ]
     }
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index a85482c..d54abfc 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -598,8 +598,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "android_webview_junit_tests",
-        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/"
       },
       {
         "merge": {
@@ -616,8 +615,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "base_junit_tests",
-        "test_id_prefix": "ninja://base:base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://base:base_junit_tests/"
       },
       {
         "merge": {
@@ -634,8 +632,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "build_junit_tests",
-        "test_id_prefix": "ninja://build/android:build_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://build/android:build_junit_tests/"
       },
       {
         "merge": {
@@ -652,8 +649,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_java_test_pagecontroller_junit_tests",
-        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/"
       },
       {
         "merge": {
@@ -670,8 +666,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_junit_tests",
-        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/"
       },
       {
         "merge": {
@@ -688,8 +683,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "components_junit_tests",
-        "test_id_prefix": "ninja://components:components_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components:components_junit_tests/"
       },
       {
         "merge": {
@@ -706,8 +700,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "content_junit_tests",
-        "test_id_prefix": "ninja://content/public/android:content_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://content/public/android:content_junit_tests/"
       },
       {
         "merge": {
@@ -724,8 +717,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "device_junit_tests",
-        "test_id_prefix": "ninja://device:device_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://device:device_junit_tests/"
       },
       {
         "merge": {
@@ -742,8 +734,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "junit_unit_tests",
-        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/"
       },
       {
         "merge": {
@@ -760,8 +751,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "keyboard_accessory_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/"
       },
       {
         "merge": {
@@ -778,8 +768,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "media_base_junit_tests",
-        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/"
       },
       {
         "merge": {
@@ -796,8 +785,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "module_installer_junit_tests",
-        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/"
       },
       {
         "merge": {
@@ -814,8 +802,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "net_junit_tests",
-        "test_id_prefix": "ninja://net/android:net_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://net/android:net_junit_tests/"
       },
       {
         "merge": {
@@ -832,8 +819,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "paint_preview_junit_tests",
-        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/"
       },
       {
         "merge": {
@@ -850,8 +836,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_check_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/"
       },
       {
         "merge": {
@@ -868,8 +853,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_manager_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/"
       },
       {
         "merge": {
@@ -886,8 +870,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "services_junit_tests",
-        "test_id_prefix": "ninja://services:services_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://services:services_junit_tests/"
       },
       {
         "merge": {
@@ -904,8 +887,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "touch_to_fill_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/"
       },
       {
         "merge": {
@@ -922,8 +904,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "ui_junit_tests",
-        "test_id_prefix": "ninja://ui:ui_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://ui:ui_junit_tests/"
       },
       {
         "merge": {
@@ -940,8 +921,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_client_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/"
       },
       {
         "merge": {
@@ -958,8 +938,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_h2o_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/"
       },
       {
         "merge": {
@@ -976,8 +955,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/"
       }
     ]
   },
@@ -19807,8 +19785,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "android_webview_junit_tests",
-        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/"
       },
       {
         "merge": {
@@ -19829,8 +19806,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "base_junit_tests",
-        "test_id_prefix": "ninja://base:base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://base:base_junit_tests/"
       },
       {
         "merge": {
@@ -19851,8 +19827,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "build_junit_tests",
-        "test_id_prefix": "ninja://build/android:build_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://build/android:build_junit_tests/"
       },
       {
         "merge": {
@@ -19873,8 +19848,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_java_test_pagecontroller_junit_tests",
-        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/"
       },
       {
         "merge": {
@@ -19895,8 +19869,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_junit_tests",
-        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/"
       },
       {
         "merge": {
@@ -19917,8 +19890,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "components_junit_tests",
-        "test_id_prefix": "ninja://components:components_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components:components_junit_tests/"
       },
       {
         "merge": {
@@ -19939,8 +19911,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "content_junit_tests",
-        "test_id_prefix": "ninja://content/public/android:content_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://content/public/android:content_junit_tests/"
       },
       {
         "merge": {
@@ -19961,8 +19932,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "device_junit_tests",
-        "test_id_prefix": "ninja://device:device_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://device:device_junit_tests/"
       },
       {
         "merge": {
@@ -19983,8 +19953,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "junit_unit_tests",
-        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/"
       },
       {
         "merge": {
@@ -20005,8 +19974,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "keyboard_accessory_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/"
       },
       {
         "merge": {
@@ -20027,8 +19995,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "media_base_junit_tests",
-        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/"
       },
       {
         "merge": {
@@ -20049,8 +20016,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "module_installer_junit_tests",
-        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/"
       },
       {
         "merge": {
@@ -20071,8 +20037,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "net_junit_tests",
-        "test_id_prefix": "ninja://net/android:net_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://net/android:net_junit_tests/"
       },
       {
         "merge": {
@@ -20093,8 +20058,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "paint_preview_junit_tests",
-        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/"
       },
       {
         "merge": {
@@ -20115,8 +20079,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_check_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/"
       },
       {
         "merge": {
@@ -20137,8 +20100,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_manager_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/"
       },
       {
         "merge": {
@@ -20159,8 +20121,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "services_junit_tests",
-        "test_id_prefix": "ninja://services:services_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://services:services_junit_tests/"
       },
       {
         "merge": {
@@ -20181,8 +20142,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "touch_to_fill_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/"
       },
       {
         "merge": {
@@ -20203,8 +20163,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "ui_junit_tests",
-        "test_id_prefix": "ninja://ui:ui_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://ui:ui_junit_tests/"
       },
       {
         "merge": {
@@ -20225,8 +20184,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_client_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/"
       },
       {
         "merge": {
@@ -20247,8 +20205,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_h2o_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/"
       },
       {
         "merge": {
@@ -20269,8 +20226,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/"
       }
     ]
   },
@@ -28195,8 +28151,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "android_webview_junit_tests",
-        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/"
       },
       {
         "args": [
@@ -28221,8 +28176,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "base_junit_tests",
-        "test_id_prefix": "ninja://base:base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://base:base_junit_tests/"
       },
       {
         "args": [
@@ -28247,8 +28201,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "build_junit_tests",
-        "test_id_prefix": "ninja://build/android:build_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://build/android:build_junit_tests/"
       },
       {
         "args": [
@@ -28273,8 +28226,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_java_test_pagecontroller_junit_tests",
-        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/"
       },
       {
         "args": [
@@ -28299,8 +28251,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_junit_tests",
-        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/"
       },
       {
         "args": [
@@ -28325,8 +28276,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "components_junit_tests",
-        "test_id_prefix": "ninja://components:components_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components:components_junit_tests/"
       },
       {
         "args": [
@@ -28394,8 +28344,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "content_junit_tests",
-        "test_id_prefix": "ninja://content/public/android:content_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://content/public/android:content_junit_tests/"
       },
       {
         "args": [
@@ -28420,8 +28369,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "device_junit_tests",
-        "test_id_prefix": "ninja://device:device_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://device:device_junit_tests/"
       },
       {
         "args": [
@@ -28446,8 +28394,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "junit_unit_tests",
-        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/"
       },
       {
         "args": [
@@ -28472,8 +28419,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "keyboard_accessory_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/"
       },
       {
         "args": [
@@ -28498,8 +28444,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "media_base_junit_tests",
-        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/"
       },
       {
         "args": [
@@ -28524,8 +28469,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "module_installer_junit_tests",
-        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/"
       },
       {
         "args": [
@@ -28591,8 +28535,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "net_junit_tests",
-        "test_id_prefix": "ninja://net/android:net_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://net/android:net_junit_tests/"
       },
       {
         "args": [
@@ -28617,8 +28560,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "paint_preview_junit_tests",
-        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/"
       },
       {
         "args": [
@@ -28643,8 +28585,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_check_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/"
       },
       {
         "args": [
@@ -28669,8 +28610,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_manager_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/"
       },
       {
         "args": [
@@ -28695,8 +28635,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "services_junit_tests",
-        "test_id_prefix": "ninja://services:services_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://services:services_junit_tests/"
       },
       {
         "args": [
@@ -28855,8 +28794,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "touch_to_fill_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/"
       },
       {
         "args": [
@@ -28881,8 +28819,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "ui_junit_tests",
-        "test_id_prefix": "ninja://ui:ui_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://ui:ui_junit_tests/"
       },
       {
         "args": [
@@ -28907,8 +28844,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_client_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/"
       },
       {
         "args": [
@@ -28933,8 +28869,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_h2o_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/"
       },
       {
         "args": [
@@ -28959,8 +28894,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/"
       }
     ],
     "scripts": [
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index ac89f65..9ab3ee3 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -339,8 +339,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "android_webview_junit_tests",
-        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -362,8 +361,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "base_junit_tests",
-        "test_id_prefix": "ninja://base:base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://base:base_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -385,8 +383,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "build_junit_tests",
-        "test_id_prefix": "ninja://build/android:build_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://build/android:build_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -408,8 +405,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_java_test_pagecontroller_junit_tests",
-        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -431,8 +427,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_junit_tests",
-        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -454,8 +449,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "components_junit_tests",
-        "test_id_prefix": "ninja://components:components_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components:components_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -477,8 +471,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "content_junit_tests",
-        "test_id_prefix": "ninja://content/public/android:content_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://content/public/android:content_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -500,8 +493,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "device_junit_tests",
-        "test_id_prefix": "ninja://device:device_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://device:device_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -523,8 +515,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "junit_unit_tests",
-        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -546,8 +537,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "keyboard_accessory_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -569,8 +559,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "media_base_junit_tests",
-        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -592,8 +581,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "module_installer_junit_tests",
-        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -615,8 +603,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "net_junit_tests",
-        "test_id_prefix": "ninja://net/android:net_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://net/android:net_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -638,8 +625,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "paint_preview_junit_tests",
-        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -661,8 +647,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_check_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -684,8 +669,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_manager_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -707,8 +691,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "services_junit_tests",
-        "test_id_prefix": "ninja://services:services_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://services:services_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -730,8 +713,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "touch_to_fill_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -753,8 +735,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "ui_junit_tests",
-        "test_id_prefix": "ninja://ui:ui_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://ui:ui_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -776,8 +757,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_client_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -799,8 +779,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_h2o_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/"
       },
       {
         "isolate_profile_data": true,
@@ -822,8 +801,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/"
       }
     ]
   },
@@ -6697,8 +6675,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "android_webview_junit_tests",
-        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/"
       },
       {
         "args": [
@@ -6723,8 +6700,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "base_junit_tests",
-        "test_id_prefix": "ninja://base:base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://base:base_junit_tests/"
       },
       {
         "args": [
@@ -6749,8 +6725,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "build_junit_tests",
-        "test_id_prefix": "ninja://build/android:build_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://build/android:build_junit_tests/"
       },
       {
         "args": [
@@ -6775,8 +6750,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_java_test_pagecontroller_junit_tests",
-        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/"
       },
       {
         "args": [
@@ -6801,8 +6775,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "chrome_junit_tests",
-        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/"
       },
       {
         "args": [
@@ -6827,8 +6800,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "components_junit_tests",
-        "test_id_prefix": "ninja://components:components_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components:components_junit_tests/"
       },
       {
         "args": [
@@ -6896,8 +6868,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "content_junit_tests",
-        "test_id_prefix": "ninja://content/public/android:content_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://content/public/android:content_junit_tests/"
       },
       {
         "args": [
@@ -6922,8 +6893,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "device_junit_tests",
-        "test_id_prefix": "ninja://device:device_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://device:device_junit_tests/"
       },
       {
         "args": [
@@ -6948,8 +6918,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "junit_unit_tests",
-        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/"
       },
       {
         "args": [
@@ -6974,8 +6943,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "keyboard_accessory_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/"
       },
       {
         "args": [
@@ -7000,8 +6968,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "media_base_junit_tests",
-        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/"
       },
       {
         "args": [
@@ -7026,8 +6993,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "module_installer_junit_tests",
-        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/"
       },
       {
         "args": [
@@ -7093,8 +7059,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "net_junit_tests",
-        "test_id_prefix": "ninja://net/android:net_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://net/android:net_junit_tests/"
       },
       {
         "args": [
@@ -7119,8 +7084,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "paint_preview_junit_tests",
-        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/"
       },
       {
         "args": [
@@ -7145,8 +7109,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_check_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/"
       },
       {
         "args": [
@@ -7171,8 +7134,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "password_manager_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/"
       },
       {
         "args": [
@@ -7197,8 +7159,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "services_junit_tests",
-        "test_id_prefix": "ninja://services:services_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://services:services_junit_tests/"
       },
       {
         "args": [
@@ -7357,8 +7318,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "touch_to_fill_junit_tests",
-        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/"
       },
       {
         "args": [
@@ -7383,8 +7343,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "ui_junit_tests",
-        "test_id_prefix": "ninja://ui:ui_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://ui:ui_junit_tests/"
       },
       {
         "args": [
@@ -7409,8 +7368,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_client_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/"
       },
       {
         "args": [
@@ -7435,8 +7393,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_h2o_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/"
       },
       {
         "args": [
@@ -7461,8 +7418,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "webapk_shell_apk_junit_tests",
-        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/",
-        "use_isolated_scripts_api": true
+        "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/"
       }
     ],
     "scripts": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index e0588a6..c6f7c67 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -47891,21 +47891,13 @@
         "test_id_prefix": "ninja://:chrome_wpt_tests/"
       },
       {
-        "args": [
-          "--test-type",
-          "testharness",
-          "reftest",
-          "crashtest",
-          "print-reftest",
-          "--additional-driver-flag=--headless=old"
-        ],
         "merge": {
           "args": [
             "--verbose"
           ],
           "script": "//third_party/blink/tools/merge_web_test_results.py"
         },
-        "name": "chrome_wpt_tests_old_headless",
+        "name": "headless_shell_wpt_tests",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
@@ -47915,10 +47907,11 @@
           "dimensions": {
             "os": "Ubuntu-22.04"
           },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 10
         },
-        "test": "chrome_wpt_tests",
-        "test_id_prefix": "ninja://:chrome_wpt_tests/"
+        "test": "headless_shell_wpt",
+        "test_id_prefix": "ninja://:headless_shell_wpt/"
       }
     ]
   },
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index e7685cd..77d5e27 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -905,6 +905,14 @@
     "label": "//headless:headless_browsertests",
     "type": "console_test_launcher",
   },
+  "headless_shell_wpt": {
+    "label": "//:headless_shell_wpt",
+    "type": "generated_script",
+    "args": [
+      "--results-directory",
+      "${ISOLATED_OUTDIR}",
+    ],
+  },
   "headless_unittests": {
     "label": "//headless:headless_unittests",
     "type": "console_test_launcher",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 725c435..2f2279e 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1041,7 +1041,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'base_junit_tests': {
         'mixins': [
@@ -1056,7 +1055,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'build_junit_tests': {
         'mixins': [
@@ -1071,7 +1069,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'chrome_java_test_pagecontroller_junit_tests': {
         'mixins': [
@@ -1086,7 +1083,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'chrome_junit_tests': {
         'mixins': [
@@ -1101,7 +1097,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'components_junit_tests': {
         'mixins': [
@@ -1116,7 +1111,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'content_junit_tests': {
         'mixins': [
@@ -1131,7 +1125,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'device_junit_tests': {
         'mixins': [
@@ -1146,7 +1139,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'junit_unit_tests': {
         'mixins': [
@@ -1161,7 +1153,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'keyboard_accessory_junit_tests': {
         'mixins': [
@@ -1176,7 +1167,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'media_base_junit_tests': {
         'mixins': [
@@ -1191,7 +1181,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'module_installer_junit_tests': {
         'mixins': [
@@ -1206,7 +1195,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'net_junit_tests': {
         'mixins': [
@@ -1221,7 +1209,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'paint_preview_junit_tests': {
         'mixins': [
@@ -1236,7 +1223,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'password_check_junit_tests': {
         'mixins': [
@@ -1251,7 +1237,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'password_manager_junit_tests': {
         'mixins': [
@@ -1266,7 +1251,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'services_junit_tests': {
         'mixins': [
@@ -1281,7 +1265,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'touch_to_fill_junit_tests': {
         'mixins': [
@@ -1296,7 +1279,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'ui_junit_tests': {
         'mixins': [
@@ -1311,7 +1293,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'webapk_client_junit_tests': {
         'mixins': [
@@ -1326,7 +1307,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'webapk_shell_apk_h2o_junit_tests': {
         'mixins': [
@@ -1341,7 +1321,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
       'webapk_shell_apk_junit_tests': {
         'mixins': [
@@ -1356,7 +1335,6 @@
           'walleye',
           'pie_fleet',
         ],
-        'use_isolated_scripts_api': True,
       },
     },
 
@@ -1649,33 +1627,6 @@
       },
     },
 
-    'chromium_wpt_tests_old_headless_isolated_scripts': {
-      'chrome_wpt_tests_old_headless': {
-        'test': 'chrome_wpt_tests',
-        'results_handler': 'layout tests',
-        'mixins': [
-          'has_native_resultdb_integration',
-        ],
-        'args': [
-          '--test-type',
-          'testharness',
-          'reftest',
-          'crashtest',
-          'print-reftest',
-          '--additional-driver-flag=--headless=old',
-        ],
-        'swarming': {
-          'shards': 1,
-        },
-        'merge': {
-          'script': '//third_party/blink/tools/merge_web_test_results.py',
-          'args': [
-            '--verbose',
-          ],
-        },
-      },
-    },
-
     'clang_tot_gtests': {
       'base_unittests': {},
     },
@@ -4118,6 +4069,25 @@
       'headless_unittests': {},
     },
 
+    'headless_shell_wpt_tests_isolated_scripts': {
+      'headless_shell_wpt_tests': {
+        'test': 'headless_shell_wpt',
+        'results_handler': 'layout tests',
+        'mixins': [
+          'has_native_resultdb_integration',
+        ],
+        'swarming': {
+          'shards': 10,
+        },
+        'merge': {
+          'script': '//third_party/blink/tools/merge_web_test_results.py',
+          'args': [
+            '--verbose',
+          ],
+        },
+      },
+    },
+
     'ios_blink_tests': {
       'absl_hardening_tests': {},
       'angle_unittests': {
@@ -6042,7 +6012,7 @@
     'chrome_wpt_tests_three_modes': [
       'chromium_wpt_tests_headful_isolated_scripts',
       'chromium_wpt_tests_isolated_scripts',
-      'chromium_wpt_tests_old_headless_isolated_scripts',
+      'headless_shell_wpt_tests_isolated_scripts',
     ],
 
     'chromeos_device_no_gtests': [
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index 494dcf5..4d653bf 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -513,7 +513,7 @@
 
     if (use_dawn) {
       deps += [
-        "//third_party/dawn/src/dawn:cpp",
+        "//third_party/dawn/include/dawn:cpp_headers",
         "//third_party/dawn/src/dawn:proc",
         "//third_party/dawn/src/dawn/native",
       ]
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 248f520..43446172 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5272,21 +5272,6 @@
             ]
         }
     ],
-    "CreateNewTabInitializeRenderer": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Nonstable_20230609",
-                    "enable_features": [
-                        "CreateNewTabInitializeRenderer"
-                    ]
-                }
-            ]
-        }
-    ],
     "CreateNotificationsAcceptedClientSafeBrowsingReports": [
         {
             "platforms": [
@@ -5366,21 +5351,6 @@
             ]
         }
     ],
-    "CrosDeviceActiveClientChurnObservationNewDeviceMetadata": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DeviceActiveClientChurnObservationNewDeviceMetadata"
-                    ]
-                }
-            ]
-        }
-    ],
     "CrosLazyLoginWebUI": [
         {
             "platforms": [
@@ -16481,22 +16451,6 @@
     "SafeBrowsingHashPrefixRealTimeLookups": [
         {
             "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "SafeBrowsingHashPrefixRealTimeLookupsRelayUrl": "https://google-ohttp-relay-safebrowsing.fastly-edge.com/"
-                    },
-                    "enable_features": [
-                        "SafeBrowsingHashPrefixRealTimeLookups"
-                    ]
-                }
-            ]
-        },
-        {
-            "platforms": [
                 "android"
             ],
             "experiments": [
@@ -18421,7 +18375,8 @@
     "SkiaGraphite": [
         {
             "platforms": [
-                "mac"
+                "mac",
+                "windows"
             ],
             "experiments": [
                 {
diff --git a/third_party/angle b/third_party/angle
index 313c73c..d71b8ee 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 313c73c3f6a056a049c03c4f928ab5d83f17a77a
+Subproject commit d71b8ee0f0e26b14a8fa642460df2635c2d7db2f
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_object_builder.cc b/third_party/blink/renderer/bindings/core/v8/v8_object_builder.cc
index 73279ca5..c3dc0e7 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_object_builder.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_object_builder.cc
@@ -36,6 +36,16 @@
   return *this;
 }
 
+V8ObjectBuilder& V8ObjectBuilder::AddNumberOrNull(const StringView& name,
+                                                  std::optional<double> value) {
+  if (value.has_value()) {
+    AddInternal(name, v8::Number::New(script_state_->GetIsolate(), *value));
+  } else {
+    AddInternal(name, v8::Null(script_state_->GetIsolate()));
+  }
+  return *this;
+}
+
 V8ObjectBuilder& V8ObjectBuilder::AddInteger(const StringView& name,
                                              uint64_t value) {
   AddInternal(name,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_object_builder.h b/third_party/blink/renderer/bindings/core/v8/v8_object_builder.h
index 06f469c..6a75f186 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_object_builder.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_object_builder.h
@@ -29,6 +29,8 @@
   V8ObjectBuilder& AddNull(const StringView& name);
   V8ObjectBuilder& AddBoolean(const StringView& name, bool value);
   V8ObjectBuilder& AddNumber(const StringView& name, double value);
+  V8ObjectBuilder& AddNumberOrNull(const StringView& name,
+                                   std::optional<double> value);
   V8ObjectBuilder& AddInteger(const StringView& name, uint64_t value);
   V8ObjectBuilder& AddString(const StringView& name, const StringView& value);
   V8ObjectBuilder& AddStringOrNull(const StringView& name,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
index 8b64483..0d8f584 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
@@ -161,7 +161,7 @@
       resource->SetSerializedCachedMetadata(serialized_metadata);
     }
     StringUTF8Adaptor code_utf8(code.value());
-    resource->AppendData(code_utf8.data(), code_utf8.size());
+    resource->AppendData(code_utf8);
     resource->FinishForTest();
 
     return resource;
diff --git a/third_party/blink/renderer/core/html/image_document.cc b/third_party/blink/renderer/core/html/image_document.cc
index e392e02..a213c1db 100644
--- a/third_party/blink/renderer/core/html/image_document.cc
+++ b/third_party/blink/renderer/core/html/image_document.cc
@@ -26,6 +26,8 @@
 
 #include <limits>
 
+#include "base/compiler_specific.h"
+#include "base/containers/span.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/renderer/core/css/css_color.h"
 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
@@ -165,8 +167,12 @@
   CHECK_LE(length, std::numeric_limits<unsigned>::max());
   // If decoding has already failed, there's no point in sending additional
   // data to the ImageResource.
-  if (image_resource_->GetStatus() != ResourceStatus::kDecodeError)
-    image_resource_->AppendData(data, length);
+  if (image_resource_->GetStatus() != ResourceStatus::kDecodeError) {
+    image_resource_->AppendData(
+        // SAFETY: The caller must ensure `data` points to `length` bytes.
+        // TODO(crbug.com/40284755): Spanify this method.
+        UNSAFE_BUFFERS(base::span(data, length)));
+  }
 
   if (!IsDetached())
     GetDocument()->ImageUpdated();
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node_test.cc b/third_party/blink/renderer/core/layout/inline/inline_node_test.cc
index fe18fcfd..c620f4b 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_node_test.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_node_test.cc
@@ -520,10 +520,13 @@
     {"Hello <img>.", {50, 80}, "", "img { width: 1em; }"},
     {"Hello <img>.", {50, 120}, "", "img { width: 5em; }"},
     {"Hello <img>.", {60, 130}, "", "img { width: 6em; }"},
-    // `text-indent.
+    // `text-indent`.
     {"6 12345 12", {60, 150}, "text-indent: 5em"},
     {"6 1234567 12", {70, 170}, "text-indent: 5em"},
-    // Negative `text-indent.
+    // `text-indent` with hyphenations.
+    // The "hy-" with the indent should be longest.
+    {"hyphenation a", {60, 160}, "hyphens: auto; text-indent: 3em", "", "en"},
+    // Negative `text-indent`.
     {"43210123 1234 12", {40, 110}, "text-indent: -5em"},
     {"4321012345 1234 12", {50, 130}, "text-indent: -5em"},
     {"432 012 1", {30, 40}, "text-indent: -5em"},
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.cc b/third_party/blink/renderer/core/loader/resource/image_resource.cc
index cf594f1..b630b71a3 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.cc
@@ -28,6 +28,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/compiler_specific.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/task/single_thread_task_runner.h"
@@ -332,12 +333,13 @@
   return GetContent()->ResourceBuffer();
 }
 
-void ImageResource::AppendData(const char* data, size_t length) {
-  v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(length);
+void ImageResource::AppendData(base::span<const char> data) {
+  v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(data.size());
   if (multipart_parser_) {
-    multipart_parser_->AppendData(data, base::checked_cast<wtf_size_t>(length));
+    multipart_parser_->AppendData(data.data(),
+                                  base::checked_cast<wtf_size_t>(data.size()));
   } else {
-    Resource::AppendData(data, length);
+    Resource::AppendData(data);
 
     // Update the image immediately if needed.
     //
@@ -514,7 +516,11 @@
 
 void ImageResource::MultipartDataReceived(const char* bytes, size_t size) {
   DCHECK(multipart_parser_);
-  Resource::AppendData(bytes, size);
+  Resource::AppendData(
+      // SAFETY: The caller must ensure `bytes` points to `size` elements.
+      // TODO(crbug.com/40284755): Make this method take a span to capture the
+      // invariant.
+      UNSAFE_BUFFERS(base::span(bytes, size)));
 }
 
 bool ImageResource::IsAccessAllowed(
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.h b/third_party/blink/renderer/core/loader/resource/image_resource.h
index 26e534e69..d47f91c 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.h
@@ -85,7 +85,7 @@
   scoped_refptr<const SharedBuffer> ResourceBuffer() const override;
   void NotifyStartLoad() override;
   void ResponseReceived(const ResourceResponse&) override;
-  void AppendData(const char*, size_t) override;
+  void AppendData(base::span<const char>) override;
   void Finish(base::TimeTicks finish_time,
               base::SingleThreadTaskRunner*) override;
   void FinishAsError(const ResourceError&,
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
index be4eba4..0abe271 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
@@ -31,6 +31,8 @@
 #include "third_party/blink/renderer/core/loader/resource/image_resource.h"
 
 #include <memory>
+#include <string_view>
+
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -178,13 +180,13 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xd9};
 
-constexpr char kSvgImage[] =
+constexpr std::string_view kSvgImage =
     "<svg width=\"200\" height=\"200\" xmlns=\"http://www.w3.org/2000/svg\" "
     "xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
     "<rect x=\"0\" y=\"0\" width=\"100px\" height=\"100px\" fill=\"red\"/>"
     "</svg>";
 
-constexpr char kSvgImage2[] =
+constexpr std::string_view kSvgImage2 =
     "<svg width=\"300\" height=\"300\" xmlns=\"http://www.w3.org/2000/svg\" "
     "xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
     "<rect x=\"0\" y=\"0\" width=\"200px\" height=\"200px\" fill=\"green\"/>"
@@ -196,7 +198,7 @@
   return test::CoreTestDataPath("cancelTest.html");
 }
 
-constexpr char kSvgImageWithSubresource[] =
+constexpr std::string_view kSvgImageWithSubresource =
     "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"198\" height=\"100\">"
     "<style>"
     "  <![CDATA[@font-face{font-family:\"test\"; "
@@ -208,14 +210,13 @@
 void ReceiveResponse(ImageResource* image_resource,
                      const KURL& url,
                      const char* mime_type,
-                     const char* data,
-                     size_t data_size) {
+                     base::span<const char> data) {
   ResourceResponse resource_response(url);
   resource_response.SetMimeType(AtomicString(mime_type));
   resource_response.SetHttpStatusCode(200);
   image_resource->NotifyStartLoad();
   image_resource->ResponseReceived(resource_response);
-  image_resource->AppendData(data, data_size);
+  image_resource->AppendData(data);
   image_resource->FinishForTest();
 }
 
@@ -268,10 +269,10 @@
   EXPECT_EQ("multipart/x-mixed-replace",
             image_resource->GetResponse().MimeType());
 
-  const char kFirstPart[] =
+  const std::string_view kFirstPart =
       "--boundary\n"
       "Content-Type: image/svg+xml\n\n";
-  image_resource->AppendData(kFirstPart, strlen(kFirstPart));
+  image_resource->AppendData(kFirstPart);
   // Send the response for the first real part. No image or data buffer is
   // created.
   EXPECT_FALSE(image_resource->ResourceBuffer());
@@ -280,12 +281,12 @@
   EXPECT_FALSE(observer->ImageNotifyFinishedCalled());
   EXPECT_EQ("image/svg+xml", image_resource->GetResponse().MimeType());
 
-  const char kSecondPart[] =
+  const std::string_view kSecondPart =
       "<svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'><rect "
       "width='1' height='1' fill='green'/></svg>\n";
   // The first bytes arrive. The data buffer is created, but no image is
   // created.
-  image_resource->AppendData(kSecondPart, strlen(kSecondPart));
+  image_resource->AppendData(kSecondPart);
   EXPECT_TRUE(image_resource->ResourceBuffer());
   EXPECT_FALSE(image_resource->GetContent()->HasImage());
   EXPECT_EQ(0, observer->ImageChangedCount());
@@ -298,10 +299,10 @@
   EXPECT_EQ(0, observer2->ImageChangedCount());
   EXPECT_FALSE(observer2->ImageNotifyFinishedCalled());
 
-  const char kThirdPart[] = "--boundary";
-  image_resource->AppendData(kThirdPart, strlen(kThirdPart));
+  const std::string_view kThirdPart = "--boundary";
+  image_resource->AppendData(kThirdPart);
   ASSERT_TRUE(image_resource->ResourceBuffer());
-  EXPECT_EQ(strlen(kSecondPart) - 1, image_resource->ResourceBuffer()->size());
+  EXPECT_EQ(kSecondPart.size() - 1, image_resource->ResourceBuffer()->size());
 
   // This part finishes. The image is created, callbacks are sent, and the data
   // buffer is cleared.
@@ -351,13 +352,12 @@
       /*cached_metadata=*/std::nullopt);
   EXPECT_FALSE(image_resource->GetContent()->HasImage());
 
-  const char kBoundary[] = "--boundary\n";
-  const char kContentType[] = "Content-Type: image/jpeg\n\n";
-  image_resource->AppendData(kBoundary, strlen(kBoundary));
-  image_resource->AppendData(kContentType, strlen(kContentType));
-  image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
-                             sizeof(kJpegImage));
-  image_resource->AppendData(kBoundary, strlen(kBoundary));
+  const std::string_view kBoundary = "--boundary\n";
+  const std::string_view kContentType = "Content-Type: image/jpeg\n\n";
+  image_resource->AppendData(kBoundary);
+  image_resource->AppendData(kContentType);
+  image_resource->AppendData(base::as_chars(base::span(kJpegImage)));
+  image_resource->AppendData(kBoundary);
   image_resource->Loader()->DidFinishLoading(base::TimeTicks(), 0, 0, 0);
   EXPECT_TRUE(image_resource->GetContent()->HasImage());
   EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
@@ -438,8 +438,7 @@
   resource_response.SetMimeType(AtomicString("image/jpeg"));
   resource_response.SetExpectedContentLength(sizeof(kJpegImage));
   image_resource->ResponseReceived(resource_response);
-  image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
-                             sizeof(kJpegImage));
+  image_resource->AppendData(base::as_chars(base::span(kJpegImage)));
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
   EXPECT_EQ(ResourceStatus::kPending, image_resource->GetStatus());
 
@@ -469,8 +468,7 @@
   resource_response.SetMimeType(AtomicString("image/jpeg"));
   resource_response.SetExpectedContentLength(sizeof(kJpegImage));
   image_resource->ResponseReceived(resource_response);
-  image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
-                             sizeof(kJpegImage));
+  image_resource->AppendData(base::as_chars(base::span(kJpegImage)));
   image_resource->FinishForTest();
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -507,8 +505,7 @@
   resource_response.SetMimeType(AtomicString("image/jpeg"));
   resource_response.SetExpectedContentLength(sizeof(kJpegImage));
   image_resource->ResponseReceived(resource_response);
-  image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage),
-                             sizeof(kJpegImage));
+  image_resource->AppendData(base::as_chars(base::span(kJpegImage)));
   image_resource->FinishForTest();
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -524,8 +521,7 @@
   auto* observer = MakeGarbageCollected<MockImageResourceObserver>(
       image_resource->GetContent());
 
-  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
-                  strlen(kSvgImage));
+  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -542,7 +538,7 @@
       image_resource->GetContent());
 
   ReceiveResponse(image_resource, url, "image/svg+xml",
-                  kSvgImageWithSubresource, strlen(kSvgImageWithSubresource));
+                  kSvgImageWithSubresource);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -587,8 +583,7 @@
       image_resource->GetContent());
 
   ReceiveResponse(image_resource, url, "image/jpeg",
-                  reinterpret_cast<const char*>(kJpegImage),
-                  sizeof(kJpegImage));
+                  base::as_chars(base::span(kJpegImage)));
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -623,8 +618,7 @@
   auto* observer = MakeGarbageCollected<MockImageResourceObserver>(
       image_resource->GetContent());
 
-  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
-                  strlen(kSvgImage));
+  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -657,8 +651,7 @@
       image_resource->GetContent());
 
   ReceiveResponse(image_resource, url, "image/jpeg",
-                  reinterpret_cast<const char*>(kJpegImage),
-                  sizeof(kJpegImage));
+                  base::as_chars(base::span(kJpegImage)));
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -672,8 +665,7 @@
 
   image_resource->SetRevalidatingRequest(ResourceRequest(url));
   ReceiveResponse(image_resource, url, "image/jpeg",
-                  reinterpret_cast<const char*>(kJpegImage2),
-                  sizeof(kJpegImage2));
+                  base::as_chars(base::span(kJpegImage2)));
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -692,8 +684,7 @@
       image_resource->GetContent());
 
   ReceiveResponse(image_resource, url, "image/jpeg",
-                  reinterpret_cast<const char*>(kJpegImage),
-                  sizeof(kJpegImage));
+                  base::as_chars(base::span(kJpegImage)));
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -706,8 +697,7 @@
             image_resource->GetContent()->GetImage()->height());
 
   image_resource->SetRevalidatingRequest(ResourceRequest(url));
-  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
-                  strlen(kSvgImage));
+  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -725,8 +715,7 @@
   auto* observer = MakeGarbageCollected<MockImageResourceObserver>(
       image_resource->GetContent());
 
-  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
-                  strlen(kSvgImage));
+  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -739,8 +728,7 @@
 
   image_resource->SetRevalidatingRequest(ResourceRequest(url));
   ReceiveResponse(image_resource, url, "image/jpeg",
-                  reinterpret_cast<const char*>(kJpegImage),
-                  sizeof(kJpegImage));
+                  base::as_chars(base::span(kJpegImage)));
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -759,8 +747,7 @@
   auto* observer = MakeGarbageCollected<MockImageResourceObserver>(
       image_resource->GetContent());
 
-  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage,
-                  strlen(kSvgImage));
+  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -772,8 +759,7 @@
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
 
   image_resource->SetRevalidatingRequest(ResourceRequest(url));
-  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage2,
-                  strlen(kSvgImage2));
+  ReceiveResponse(image_resource, url, "image/svg+xml", kSvgImage2);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -792,8 +778,7 @@
   ImageResource* image_resource = ImageResource::CreateForTest(url);
 
   ReceiveResponse(image_resource, url, "image/jpeg",
-                  reinterpret_cast<const char*>(kJpegImage),
-                  sizeof(kJpegImage));
+                  base::as_chars(base::span(kJpegImage)));
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
@@ -981,9 +966,9 @@
   // image to be created (since the size is known). This was determined by
   // appending one byte at a time (with flushes) until the image was decoded.
   size_t meaningful_image_size = 280;
-  image_resource->AppendData(reinterpret_cast<const char*>(kJpegImage2),
-                             meaningful_image_size);
-  size_t bytes_sent = meaningful_image_size;
+  base::span<const char> remaining = base::as_chars(base::span(kJpegImage2));
+  image_resource->AppendData(remaining.first(meaningful_image_size));
+  remaining = remaining.subspan(meaningful_image_size);
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
   EXPECT_TRUE(image_resource->GetContent()->HasImage());
@@ -1006,14 +991,13 @@
     // increases.
     for (int i = 0; i < 5; ++i) {
       SCOPED_TRACE(i);
-      image_resource->AppendData(
-          reinterpret_cast<const char*>(kJpegImage2) + bytes_sent, 1);
+      image_resource->AppendData(remaining.first(1u));
+      remaining = remaining.subspan(1u);
 
       EXPECT_FALSE(image_resource->ErrorOccurred());
       ASSERT_TRUE(image_resource->GetContent()->HasImage());
       EXPECT_EQ(flush_count, observer->ImageChangedCount());
 
-      ++bytes_sent;
       platform->RunForPeriodSeconds(0.2001);
     }
   }
@@ -1027,9 +1011,7 @@
   EXPECT_EQ(4, observer->ImageChangedCount());
 
   // Append the rest of the data and finish (which causes another flush).
-  image_resource->AppendData(
-      reinterpret_cast<const char*>(kJpegImage2) + bytes_sent,
-      sizeof(kJpegImage2) - bytes_sent);
+  image_resource->AppendData(remaining);
   image_resource->FinishForTest();
 
   EXPECT_FALSE(image_resource->ErrorOccurred());
@@ -1049,8 +1031,7 @@
 
   // Image loaded.
   ReceiveResponse(image_resource, NullURL(), "image/jpeg",
-                  reinterpret_cast<const char*>(kJpegImage),
-                  sizeof(kJpegImage));
+                  base::as_chars(base::span(kJpegImage)));
   EXPECT_EQ(obs->ImageChangedCount(), 2);
   EXPECT_EQ(obs->Defer(), ImageResourceObserver::CanDeferInvalidation::kNo);
 
@@ -1096,20 +1077,17 @@
 
   // Test lossy WebP image.
   ImageResource* image_resource = ImageResource::CreateForTest(test_url);
-  image_resource->AppendData(reinterpret_cast<const char*>(kLossyWebPImage),
-                             sizeof(kLossyWebPImage));
+  image_resource->AppendData(base::as_chars(base::span(kLossyWebPImage)));
   EXPECT_EQ(1, image_resource->GetContent()->GetCompressionFormat());
 
   // Test lossless WebP image.
   image_resource = ImageResource::CreateForTest(test_url);
-  image_resource->AppendData(reinterpret_cast<const char*>(kLosslessWebPImage),
-                             sizeof(kLosslessWebPImage));
+  image_resource->AppendData(base::as_chars(base::span(kLosslessWebPImage)));
   EXPECT_EQ(2, image_resource->GetContent()->GetCompressionFormat());
 
   // Test extended WebP image.
   image_resource = ImageResource::CreateForTest(test_url);
-  image_resource->AppendData(reinterpret_cast<const char*>(kExtendedWebPImage),
-                             sizeof(kExtendedWebPImage));
+  image_resource->AppendData(base::as_chars(base::span(kExtendedWebPImage)));
   EXPECT_EQ(1, image_resource->GetContent()->GetCompressionFormat());
 }
 
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource_test.cc b/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
index 3c1a0dc..efa80044 100644
--- a/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/script_resource_test.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/loader/resource/script_resource.h"
 
+#include <string_view>
+
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.h"
@@ -25,8 +27,8 @@
   response.SetHttpStatusCode(200);
 
   resource->ResponseReceived(response);
-  constexpr char kData[5] = "abcd";
-  resource->AppendData(kData, strlen(kData));
+  constexpr std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
 
   auto* original_handler = resource->CacheHandler();
@@ -51,8 +53,8 @@
   response.SetHttpStatusCode(200);
 
   resource->ResponseReceived(response);
-  constexpr char kData[5] = "abcd";
-  resource->AppendData(kData, strlen(kData));
+  constexpr std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
 
   auto* original_handler = resource->CacheHandler();
@@ -79,8 +81,8 @@
   response.SetHttpStatusCode(200);
 
   resource->ResponseReceived(response);
-  constexpr char kData[5] = "abcd";
-  resource->AppendData(kData, strlen(kData));
+  constexpr std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
 
   auto* original_handler = resource->CacheHandler();
@@ -115,8 +117,8 @@
   response.SetHttpStatusCode(200);
 
   resource->ResponseReceived(response);
-  constexpr char kData[5] = "abcd";
-  resource->AppendData(kData, strlen(kData));
+  constexpr std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
 
   auto* handler = resource->CacheHandler();
@@ -140,8 +142,8 @@
   response.SetHttpStatusCode(200);
 
   resource->ResponseReceived(response);
-  constexpr char kData[5] = "abcd";
-  resource->AppendData(kData, strlen(kData));
+  constexpr std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
 
   auto* handler = resource->CacheHandler();
@@ -159,8 +161,8 @@
   response.SetShouldUseSourceHashForJSCodeCache(true);
 
   resource->ResponseReceived(response);
-  constexpr char kData[5] = "abcd";
-  resource->AppendData(kData, strlen(kData));
+  constexpr std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
 
   auto* handler = resource->CacheHandler();
diff --git a/third_party/blink/renderer/modules/geolocation/BUILD.gn b/third_party/blink/renderer/modules/geolocation/BUILD.gn
index 2c152d2..853b6231 100644
--- a/third_party/blink/renderer/modules/geolocation/BUILD.gn
+++ b/third_party/blink/renderer/modules/geolocation/BUILD.gn
@@ -16,6 +16,7 @@
     "geolocation_position_error.h",
     "geolocation_watchers.cc",
     "geolocation_watchers.h",
+    "geoposition.cc",
     "geoposition.h",
   ]
 }
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.cc b/third_party/blink/renderer/modules/geolocation/geolocation.cc
index 1051a55..fd27590b 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.cc
@@ -27,6 +27,8 @@
 
 #include "third_party/blink/renderer/modules/geolocation/geolocation.h"
 
+#include <optional>
+
 #include "base/task/single_thread_task_runner.h"
 #include "services/device/public/mojom/geoposition.mojom-blink.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
@@ -61,10 +63,16 @@
   auto* coordinates = MakeGarbageCollected<GeolocationCoordinates>(
       position.latitude, position.longitude,
       // Lowest point on land is at approximately -400 meters.
-      position.altitude > -10000., position.altitude, position.accuracy,
-      position.altitude_accuracy >= 0., position.altitude_accuracy,
-      position.heading >= 0. && position.heading <= 360., position.heading,
-      position.speed >= 0., position.speed);
+      position.altitude > -10000. ? std::make_optional(position.altitude)
+                                  : std::nullopt,
+      position.accuracy,
+      position.altitude_accuracy >= 0.
+          ? std::make_optional(position.altitude_accuracy)
+          : std::nullopt,
+      position.heading >= 0. && position.heading <= 360.
+          ? std::make_optional(position.heading)
+          : std::nullopt,
+      position.speed >= 0. ? std::optional(position.speed) : std::nullopt);
   return MakeGarbageCollected<Geoposition>(
       coordinates, ConvertTimeToEpochTimeStamp(position.timestamp));
 }
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.cc b/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.cc
index 1c1d38fa..f8d085f 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.cc
@@ -25,30 +25,20 @@
 
 #include "third_party/blink/renderer/modules/geolocation/geolocation_coordinates.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
+
 namespace blink {
 
-std::optional<double> GeolocationCoordinates::altitude() const {
-  if (can_provide_altitude_)
-    return altitude_;
-  return std::nullopt;
-}
-
-std::optional<double> GeolocationCoordinates::altitudeAccuracy() const {
-  if (can_provide_altitude_accuracy_)
-    return altitude_accuracy_;
-  return std::nullopt;
-}
-
-std::optional<double> GeolocationCoordinates::heading() const {
-  if (can_provide_heading_)
-    return heading_;
-  return std::nullopt;
-}
-
-std::optional<double> GeolocationCoordinates::speed() const {
-  if (can_provide_speed_)
-    return speed_;
-  return std::nullopt;
+ScriptValue GeolocationCoordinates::toJSON(ScriptState* script_state) const {
+  V8ObjectBuilder builder(script_state);
+  builder.AddNumber("accuracy", accuracy_);
+  builder.AddNumber("latitude", latitude_);
+  builder.AddNumber("longitude", longitude_);
+  builder.AddNumberOrNull("altitude", altitude_);
+  builder.AddNumberOrNull("altitudeAccuracy", altitude_accuracy_);
+  builder.AddNumberOrNull("heading", heading_);
+  builder.AddNumberOrNull("speed", speed_);
+  return builder.GetScriptValue();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.h b/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.h
index d205bbc..c62ae3d 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.h
+++ b/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.h
@@ -28,6 +28,7 @@
 
 #include <optional>
 
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/modules/event_modules.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
@@ -39,48 +40,36 @@
  public:
   GeolocationCoordinates(double latitude,
                          double longitude,
-                         bool provides_altitude,
-                         double altitude,
+                         std::optional<double> altitude,
                          double accuracy,
-                         bool provides_altitude_accuracy,
-                         double altitude_accuracy,
-                         bool provides_heading,
-                         double heading,
-                         bool provides_speed,
-                         double speed)
+                         std::optional<double> altitude_accuracy,
+                         std::optional<double> heading,
+                         std::optional<double> speed)
       : latitude_(latitude),
         longitude_(longitude),
         altitude_(altitude),
         accuracy_(accuracy),
         altitude_accuracy_(altitude_accuracy),
         heading_(heading),
-        speed_(speed),
-        can_provide_altitude_(provides_altitude),
-        can_provide_altitude_accuracy_(provides_altitude_accuracy),
-        can_provide_heading_(provides_heading),
-        can_provide_speed_(provides_speed) {}
+        speed_(speed) {}
 
   double latitude() const { return latitude_; }
   double longitude() const { return longitude_; }
-  std::optional<double> altitude() const;
+  std::optional<double> altitude() const { return altitude_; }
   double accuracy() const { return accuracy_; }
-  std::optional<double> altitudeAccuracy() const;
-  std::optional<double> heading() const;
-  std::optional<double> speed() const;
+  std::optional<double> altitudeAccuracy() const { return altitude_accuracy_; }
+  std::optional<double> heading() const { return heading_; }
+  std::optional<double> speed() const { return speed_; }
+  ScriptValue toJSON(ScriptState* script_state) const;
 
  private:
   double latitude_;
   double longitude_;
-  double altitude_;
+  std::optional<double> altitude_;
   double accuracy_;
-  double altitude_accuracy_;
-  double heading_;
-  double speed_;
-
-  bool can_provide_altitude_;
-  bool can_provide_altitude_accuracy_;
-  bool can_provide_heading_;
-  bool can_provide_speed_;
+  std::optional<double> altitude_accuracy_;
+  std::optional<double> heading_;
+  std::optional<double> speed_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.idl b/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.idl
index f2916d2b..3d48bf73 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.idl
+++ b/third_party/blink/renderer/modules/geolocation/geolocation_coordinates.idl
@@ -33,4 +33,5 @@
     readonly attribute double? altitudeAccuracy;
     readonly attribute double? heading;
     readonly attribute double? speed;
+    [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation_position.idl b/third_party/blink/renderer/modules/geolocation/geolocation_position.idl
index e6daf9b9..9a07d71 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation_position.idl
+++ b/third_party/blink/renderer/modules/geolocation/geolocation_position.idl
@@ -31,4 +31,5 @@
 ] interface GeolocationPosition {
     readonly attribute GeolocationCoordinates coords;
     readonly attribute EpochTimeStamp timestamp;
+    [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/modules/geolocation/geoposition.cc b/third_party/blink/renderer/modules/geolocation/geoposition.cc
new file mode 100644
index 0000000..2c1488a
--- /dev/null
+++ b/third_party/blink/renderer/modules/geolocation/geoposition.cc
@@ -0,0 +1,18 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/geolocation/geoposition.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
+
+namespace blink {
+
+ScriptValue Geoposition::toJSON(ScriptState* script_state) const {
+  V8ObjectBuilder builder(script_state);
+  builder.AddInteger("timestamp", timestamp_);
+  builder.AddV8Value("coords", coordinates_->toJSON(script_state).V8Value());
+  return builder.GetScriptValue();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/geolocation/geoposition.h b/third_party/blink/renderer/modules/geolocation/geoposition.h
index 1e723e7a..c468372 100644
--- a/third_party/blink/renderer/modules/geolocation/geoposition.h
+++ b/third_party/blink/renderer/modules/geolocation/geoposition.h
@@ -26,6 +26,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_GEOLOCATION_GEOPOSITION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_GEOLOCATION_GEOPOSITION_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/timing/epoch_time_stamp.h"
 #include "third_party/blink/renderer/modules/event_modules.h"
 #include "third_party/blink/renderer/modules/geolocation/geolocation_coordinates.h"
@@ -50,6 +51,7 @@
 
   EpochTimeStamp timestamp() const { return timestamp_; }
   GeolocationCoordinates* coords() const { return coordinates_.Get(); }
+  ScriptValue toJSON(ScriptState* script_state) const;
 
  private:
   Member<GeolocationCoordinates> coordinates_;
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.proto b/third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.proto
index 331c211..70f0cec5 100644
--- a/third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.proto
+++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_inputs.proto
@@ -200,6 +200,18 @@
   optional AacFormat format = 1;
 }
 
+enum OpusSignal {
+  AUTO = 0;
+  MUSIC = 1;
+  VOICE = 2;
+}
+
+enum OpusApplication {
+  VOIP = 0;
+  AUDIO = 1;
+  LOWDELAY = 2;
+}
+
 message OpusEncoderConfig {
   optional uint64 frame_duration = 1;
 
@@ -210,6 +222,10 @@
   optional bool useinbandfec = 4;
 
   optional bool usedtx = 5;
+
+  optional OpusSignal signal = 6;
+
+  optional OpusApplication application = 7;
 }
 
 message ConfigureAudioEncoder {
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_seed_corpus/audio_encoder/encode_opus.textproto b/third_party/blink/renderer/modules/webcodecs/fuzzer_seed_corpus/audio_encoder/encode_opus.textproto
index 69a1488..affd29e 100644
--- a/third_party/blink/renderer/modules/webcodecs/fuzzer_seed_corpus/audio_encoder/encode_opus.textproto
+++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_seed_corpus/audio_encoder/encode_opus.textproto
@@ -19,7 +19,9 @@
         complexity: 9,
         packetlossperc: 20,
         useinbandfec: true,
-        usedtx: true
+        usedtx: true,
+        signal: 'voice',
+        application: 'voip',
       }
     }
   },
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
index cf727f0..168b5f7 100644
--- a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
+++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
@@ -180,6 +180,12 @@
     if (proto.opus().has_usedtx()) {
       opus->setUsedtx(proto.opus().usedtx());
     }
+    if (proto.opus().has_signal()) {
+      opus->setSignal(ToOpusSignal(proto.opus().signal()));
+    }
+    if (proto.opus().has_application()) {
+      opus->setApplication(ToOpusApplication(proto.opus().application()));
+    }
   }
 
   return config;
@@ -270,6 +276,28 @@
   }
 }
 
+String ToOpusSignal(wc_fuzzer::OpusSignal opus_signal) {
+  switch (opus_signal) {
+    case wc_fuzzer::AUTO:
+      return "auto";
+    case wc_fuzzer::MUSIC:
+      return "music";
+    case wc_fuzzer::VOICE:
+      return "voice";
+  }
+}
+
+String ToOpusApplication(wc_fuzzer::OpusApplication opus_application) {
+  switch (opus_application) {
+    case wc_fuzzer::VOIP:
+      return "voip";
+    case wc_fuzzer::AUDIO:
+      return "audio";
+    case wc_fuzzer::LOWDELAY:
+      return "lowdelay";
+  }
+}
+
 String ToChunkType(wc_fuzzer::EncodedChunkType type) {
   switch (type) {
     case wc_fuzzer::EncodedChunkType::KEY:
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h
index d608a73..75758c6 100644
--- a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h
+++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h
@@ -112,6 +112,10 @@
 
 String ToBitrateMode(wc_fuzzer::BitrateMode bitrate_mode);
 
+String ToOpusSignal(wc_fuzzer::OpusSignal opus_signal);
+
+String ToOpusApplication(wc_fuzzer::OpusApplication opus_application);
+
 String ToAccelerationType(
     wc_fuzzer::ConfigureVideoEncoder_EncoderAccelerationPreference type);
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
index 43a43f1..8519ea9 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
@@ -30,6 +30,8 @@
 
 #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
 
+#include <string_view>
+
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -65,8 +67,8 @@
                       const ResourceLoaderOptions& options)
       : Resource(request, ResourceType::kMock, options) {}
 
-  void AppendData(const char* data, size_t len) override {
-    Resource::AppendData(data, len);
+  void AppendData(base::span<const char> data) override {
+    Resource::AppendData(data);
     SetDecodedSize(this->size());
   }
 
@@ -212,14 +214,14 @@
   MemoryCache::Get()->Remove(dummy_resource);
   EXPECT_EQ(0u, MemoryCache::Get()->size());
 
-  const char kData[6] = "abcde";
+  std::string_view kData = "abcde";
   FetchParameters params1 = FetchParameters::CreateForTest(
       ResourceRequest("data:image/jpeg,resource1"));
   Resource* resource1 = FakeDecodedResource::Fetch(params1, fetcher, nullptr);
   MemoryCache::Get()->Remove(resource1);
   if (!identifier1.empty())
     resource1->SetCacheIdentifier(identifier1);
-  resource1->AppendData(kData, 3u);
+  resource1->AppendData(kData.substr(0u, 3u));
   resource1->FinishForTest();
   FetchParameters params2 = FetchParameters::CreateForTest(
       ResourceRequest("data:image/jpeg,resource2"));
@@ -229,7 +231,7 @@
   MemoryCache::Get()->Remove(resource2);
   if (!identifier2.empty())
     resource2->SetCacheIdentifier(identifier2);
-  resource2->AppendData(kData, 4u);
+  resource2->AppendData(kData.substr(0u, 4u));
   resource2->FinishForTest();
 
   platform->test_task_runner()->PostTask(
@@ -268,7 +270,7 @@
                               const String& identifier1,
                               const String& identifier2) {
   MemoryCache::Get()->SetCapacity(0);
-  const char kData[6] = "abcde";
+  const std::string_view kData = "abcde";
   Persistent<MockResourceClient> client1 =
       MakeGarbageCollected<MockResourceClient>();
   Persistent<MockResourceClient> client2 =
@@ -279,8 +281,8 @@
   FetchParameters params2 =
       FetchParameters::CreateForTest(ResourceRequest("data:image/jpeg,bar"));
   Resource* resource2 = FakeDecodedResource::Fetch(params2, fetcher, client2);
-  resource1->AppendData(kData, 4u);
-  resource2->AppendData(kData, 4u);
+  resource1->AppendData(kData.substr(0u, 4u));
+  resource2->AppendData(kData.substr(0u, 4u));
 
   MemoryCache::Get()->SetCapacity(0);
   // Remove and re-Add the resources, with proper cache identifiers.
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
index cb9d8f7..3323b463 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -99,11 +99,11 @@
                          const ResourceLoaderOptions& options)
     : Resource(resource_request, type, options) {}
 
-void RawResource::AppendData(const char* data, size_t length) {
+void RawResource::AppendData(base::span<const char> data) {
   if (GetResourceRequest().UseStreamOnResponse())
     return;
 
-  Resource::AppendData(data, length);
+  Resource::AppendData(data);
 }
 
 class RawResource::PreloadBytesConsumerClient final
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
index b251a06..96940b4 100644
--- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
@@ -102,7 +102,7 @@
 
   // Resource implementation
   void DidAddClient(ResourceClient*) override;
-  void AppendData(const char*, size_t) override;
+  void AppendData(base::span<const char>) override;
 
   bool ShouldIgnoreHTTPStatusCodeErrors() const override { return true; }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index d914ec6..fc6b8b22 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -250,18 +250,18 @@
   }
 }
 
-void Resource::AppendData(const char* data, size_t length) {
-  TRACE_EVENT1("blink", "Resource::appendData", "length", length);
+void Resource::AppendData(base::span<const char> data) {
+  TRACE_EVENT1("blink", "Resource::appendData", "length", data.size());
   DCHECK(!IsCacheValidator());
   DCHECK(!ErrorOccurred());
   if (options_.data_buffering_policy == kBufferData) {
     if (data_)
-      data_->Append(data, length);
+      data_->Append(data);
     else
-      data_ = SharedBuffer::Create(data, length);
+      data_ = SharedBuffer::Create(data);
     SetEncodedSize(data_->size());
   }
-  NotifyDataReceived(base::span(data, length));
+  NotifyDataReceived(data);
 }
 
 void Resource::NotifyDataReceived(base::span<const char> data) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index 836b49b..aedda1a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -159,7 +159,7 @@
   void Trace(Visitor*) const override;
 
   virtual WTF::TextEncoding Encoding() const { return WTF::TextEncoding(); }
-  virtual void AppendData(const char*, size_t);
+  virtual void AppendData(base::span<const char>);
   virtual void FinishAsError(const ResourceError&,
                              base::SingleThreadTaskRunner*);
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index 675df831..fe2411e4 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -33,6 +33,8 @@
 #include <optional>
 #include <utility>
 
+#include "base/compiler_specific.h"
+#include "base/containers/span.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/memory/weak_ptr.h"
@@ -1046,7 +1048,11 @@
     observer->DidReceiveData(resource_->InspectorId(),
                              base::make_span(data, length));
   }
-  resource_->AppendData(data, length);
+  resource_->AppendData(
+      // SAFETY: `data` must point to `length` elements.
+      // TODO(crbug.com/40284755): Make this method take a span to capture it in
+      // the type system.
+      UNSAFE_BUFFERS(base::span(data, length)));
 
   // This value should not be exposed for opaque responses.
   if (resource_->response_.WasFetchedViaServiceWorker() &&
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
index 4c81f39..ec5384b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
 
+#include <string_view>
+
 #include "base/test/task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/platform.h"
@@ -110,8 +112,8 @@
   ResourceResponse response(url);
   response.SetHttpStatusCode(200);
   resource->ResponseReceived(response);
-  const char kData[5] = "abcd";
-  resource->AppendData(kData, 4);
+  const std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
   MemoryCache::Get()->Add(resource);
 
@@ -132,7 +134,7 @@
   EXPECT_FALSE(resource->ResourceBuffer());
   EXPECT_EQ(resource, MemoryCache::Get()->ResourceForURL(url));
 
-  resource->AppendData(kData, 4);
+  resource->AppendData(kData);
 
   EXPECT_FALSE(client->NotifyFinishedCalled());
 
@@ -152,8 +154,8 @@
   ResourceResponse response(url);
   response.SetHttpStatusCode(200);
   resource->ResponseReceived(response);
-  const char kData[5] = "abcd";
-  resource->AppendData(kData, 4);
+  const std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
   MemoryCache::Get()->Add(resource);
 
@@ -318,8 +320,8 @@
   ResourceResponse response(url);
   response.SetHttpStatusCode(200);
   resource->ResponseReceived(response);
-  const char kData[5] = "abcd";
-  resource->AppendData(kData, 4);
+  const std::string_view kData = "abcd";
+  resource->AppendData(kData);
   resource->FinishForTest();
   MemoryCache::Get()->Add(resource);
 
@@ -357,8 +359,8 @@
   revalidating_response.SetHttpStatusCode(200);
   resource->ResponseReceived(revalidating_response);
 
-  const char kData2[4] = "xyz";
-  resource->AppendData(kData2, 3);
+  const std::string_view kData2 = "xyz";
+  resource->AppendData(kData2);
   resource->FinishForTest();
   EXPECT_FALSE(resource->IsCacheValidator());
   EXPECT_FALSE(resource->HasSuccessfulRevalidation());
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 1e99bc4..c023047 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1780,6 +1780,7 @@
       // Protected Audience ad auction config.
       name: "FledgeDeprecatedRenderURLReplacements",
       public: true,
+      status: "test",
     },
     {
       name: "FledgeDirectFromSellerSignalsHeaderAdSlot",
@@ -2863,7 +2864,7 @@
     {
       name: "PagePopup",
       // Android does not have support for PagePopup
-      status: {"Android": "", "default": "stable"},
+      status: {"Android": "", "iOS": "", "default": "stable"},
     },
     {
       name: "PageRevealEvent",
diff --git a/third_party/blink/renderer/platform/wtf/shared_buffer.cc b/third_party/blink/renderer/platform/wtf/shared_buffer.cc
index 6168f1cc..cbebda93 100644
--- a/third_party/blink/renderer/platform/wtf/shared_buffer.cc
+++ b/third_party/blink/renderer/platform/wtf/shared_buffer.cc
@@ -28,10 +28,12 @@
 
 #include <memory>
 
+#include "base/compiler_specific.h"
 #include "base/numerics/safe_conversions.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
 #include "third_party/blink/renderer/platform/wtf/text/utf8.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
 
 namespace WTF {
 
@@ -101,12 +103,14 @@
 
 SharedBuffer::SharedBuffer(wtf_size_t size) : size_(size), buffer_(size) {}
 
-SharedBuffer::SharedBuffer(const char* data, wtf_size_t size) : size_(size) {
-  buffer_.Append(data, size);
+SharedBuffer::SharedBuffer(base::span<const char> data)
+    : size_(base::checked_cast<wtf_size_t>(data.size())) {
+  // TODO(crbug.com/40284755): Spanify `Vector::Append`.
+  buffer_.Append(data.data(), base::checked_cast<wtf_size_t>(data.size()));
 }
 
-SharedBuffer::SharedBuffer(const unsigned char* data, wtf_size_t size)
-    : SharedBuffer(reinterpret_cast<const char*>(data), size) {}
+SharedBuffer::SharedBuffer(base::span<const unsigned char> data)
+    : SharedBuffer(base::as_chars(data)) {}
 
 SharedBuffer::~SharedBuffer() = default;
 
@@ -124,32 +128,37 @@
 
 void SharedBuffer::Append(const SharedBuffer& data) {
   for (const auto& span : data)
-    Append(span.data(), span.size());
+    Append(span);
 }
 
-void SharedBuffer::AppendInternal(const char* data, size_t length) {
-  if (!length)
-    return;
-
-  DCHECK_GE(size_, buffer_.size());
-  size_t position_in_segment = OffsetInSegment(size_ - buffer_.size());
-  size_ += length;
-
-  if (size_ <= kSegmentSize) {
-    // No need to use segments for small resource data.
-    buffer_.Append(data, static_cast<wtf_size_t>(length));
+void SharedBuffer::Append(base::span<const char> data) {
+  if (data.empty()) {
     return;
   }
 
-  while (length > 0) {
+  DCHECK_GE(size_, buffer_.size());
+  size_t position_in_segment = OffsetInSegment(size_ - buffer_.size());
+  size_ += data.size();
+
+  if (size_ <= kSegmentSize) {
+    // No need to use segments for small resource data.
+    buffer_.Append(data.data(), static_cast<wtf_size_t>(data.size()));
+    return;
+  }
+
+  while (!data.empty()) {
     if (!position_in_segment)
       segments_.push_back(CreateSegment());
 
-    size_t bytes_to_copy = std::min(length, kSegmentSize - position_in_segment);
-    memcpy(segments_.back().get() + position_in_segment, data, bytes_to_copy);
+    size_t bytes_to_copy =
+        std::min(data.size(), kSegmentSize - position_in_segment);
+    auto [to_copy, rest] = data.split_at(bytes_to_copy);
+    // SAFETY: Every segment has size `kSegmentSize`.
+    UNSAFE_BUFFERS(base::span(segments_.back().get(), kSegmentSize))
+        .subspan(position_in_segment, bytes_to_copy)
+        .copy_from(to_copy);
 
-    data += bytes_to_copy;
-    length -= bytes_to_copy;
+    data = rest;
     position_in_segment = 0;
   }
 }
diff --git a/third_party/blink/renderer/platform/wtf/shared_buffer.h b/third_party/blink/renderer/platform/wtf/shared_buffer.h
index 2095c46..88709f9 100644
--- a/third_party/blink/renderer/platform/wtf/shared_buffer.h
+++ b/third_party/blink/renderer/platform/wtf/shared_buffer.h
@@ -31,6 +31,7 @@
 #include <vector>
 
 #include "base/check_op.h"
+#include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/ranges/algorithm.h"
@@ -99,6 +100,15 @@
     return base::AdoptRef(new SharedBuffer);
   }
 
+  static scoped_refptr<SharedBuffer> Create(base::span<const char> data) {
+    return base::AdoptRef(new SharedBuffer(data));
+  }
+
+  static scoped_refptr<SharedBuffer> Create(
+      base::span<const unsigned char> data) {
+    return base::AdoptRef(new SharedBuffer(data));
+  }
+
   HAS_STRICTLY_TYPED_ARG
   static scoped_refptr<SharedBuffer> Create(STRICTLY_TYPED_ARG(size)) {
     ALLOW_NUMERIC_ARG_TYPES_PROMOTABLE_TO(size_t);
@@ -110,16 +120,20 @@
   static scoped_refptr<SharedBuffer> Create(const char* data,
                                             STRICTLY_TYPED_ARG(size)) {
     ALLOW_NUMERIC_ARG_TYPES_PROMOTABLE_TO(size_t);
-    return base::AdoptRef(
-        new SharedBuffer(data, base::checked_cast<wtf_size_t>(size)));
+    return Create(
+        // SAFETY: The caller must ensure `data` points to `size` elements.
+        // TODO(crbug.com/40284755): Remove this in favor of the span versions.
+        UNSAFE_BUFFERS(base::span(data, size)));
   }
 
   HAS_STRICTLY_TYPED_ARG
   static scoped_refptr<SharedBuffer> Create(const unsigned char* data,
                                             STRICTLY_TYPED_ARG(size)) {
     ALLOW_NUMERIC_ARG_TYPES_PROMOTABLE_TO(size_t);
-    return base::AdoptRef(
-        new SharedBuffer(data, base::checked_cast<wtf_size_t>(size)));
+    return Create(
+        // SAFETY: The caller must ensure `data` points to `size` elements.
+        // TODO(crbug.com/40284755): Remove this in favor of the span versions.
+        UNSAFE_BUFFERS(base::span(data, size)));
   }
 
   static scoped_refptr<SharedBuffer> AdoptVector(Vector<char>&);
@@ -136,17 +150,29 @@
 
   void Append(const SharedBuffer&);
 
+  // TODO(crbug.com/40284755): Remove the pointer-based methods in favor of span
+  // ones.
   HAS_STRICTLY_TYPED_ARG
   void Append(const char* data, STRICTLY_TYPED_ARG(size)) {
     ALLOW_NUMERIC_ARG_TYPES_PROMOTABLE_TO(size_t);
-    AppendInternal(data, size);
+    Append(
+        // SAFETY: The caller must ensure `data` points to `size` elements.
+        // TODO(crbug.com/40284755): Remove this in favor of the span versions.
+        UNSAFE_BUFFERS(base::span(data, size)));
   }
   HAS_STRICTLY_TYPED_ARG
   void Append(const unsigned char* data, STRICTLY_TYPED_ARG(size)) {
     ALLOW_NUMERIC_ARG_TYPES_PROMOTABLE_TO(size_t);
-    AppendInternal(reinterpret_cast<const char*>(data), size);
+    Append(
+        // SAFETY: The caller must ensure `data` points to `size` elements.
+        // TODO(crbug.com/40284755): Remove this in favor of the span versions.
+        UNSAFE_BUFFERS(base::span(data, size)));
   }
-  void Append(const Vector<char>& data) { Append(data.data(), data.size()); }
+
+  void Append(base::span<const char> data);
+  void Append(base::span<const unsigned char> data) {
+    Append(base::as_chars(data));
+  }
 
   void Clear();
 
@@ -209,13 +235,12 @@
 
   SharedBuffer();
   explicit SharedBuffer(wtf_size_t);
-  SharedBuffer(const char*, wtf_size_t);
-  SharedBuffer(const unsigned char*, wtf_size_t);
+  explicit SharedBuffer(base::span<const char>);
+  explicit SharedBuffer(base::span<const unsigned char>);
 
   // See SharedBuffer::data().
   void MergeSegmentsIntoBuffer();
 
-  void AppendInternal(const char* data, size_t);
   bool GetBytesInternal(void* dest, size_t) const;
   Iterator GetIteratorAtInternal(size_t position) const;
   size_t GetLastSegmentSize() const {
diff --git a/third_party/blink/tools/blinkpy/wpt_tests/product.py b/third_party/blink/tools/blinkpy/wpt_tests/product.py
index 6e585bef..44e2552 100644
--- a/third_party/blink/tools/blinkpy/wpt_tests/product.py
+++ b/third_party/blink/tools/blinkpy/wpt_tests/product.py
@@ -37,7 +37,9 @@
     respective classes.
     """
     product_registry = {}
-    product_classes = [Chrome, ContentShell, ChromeiOS, ChromeAndroid, WebView]
+    product_classes = [
+        Chrome, HeadlessShell, ContentShell, ChromeiOS, ChromeAndroid, WebView
+    ]
     for product_cls in product_classes:
         names = [product_cls.name] + product_cls.aliases
         product_registry.update((name, product_cls) for name in names)
@@ -112,6 +114,10 @@
         }
 
 
+class HeadlessShell(Chrome):
+    name = 'headless_shell'
+
+
 class ContentShell(Product):
     name = 'content_shell'
 
diff --git a/third_party/blink/tools/blinkpy/wpt_tests/wpt_adapter.py b/third_party/blink/tools/blinkpy/wpt_tests/wpt_adapter.py
index fa17900..d97a332f 100644
--- a/third_party/blink/tools/blinkpy/wpt_tests/wpt_adapter.py
+++ b/third_party/blink/tools/blinkpy/wpt_tests/wpt_adapter.py
@@ -170,7 +170,7 @@
         else:
             port = host.port_factory.get(port_name, options)
 
-        if options.product == 'chrome':
+        if options.product in ['chrome', 'headless_shell']:
             port.set_option_default('driver_name', port.CHROME_NAME)
         product = make_product(port, options)
         return WPTAdapter(product, port, options, tests)
@@ -233,7 +233,8 @@
                 mozlog.commandline.log_formatters[name][1],
             )
 
-        runner_options = parser.parse_args(['--product', self.product.name])
+        product_name = 'chrome' if self.product.name == 'headless_shell' else self.product.name
+        runner_options = parser.parse_args(['--product', product_name])
         runner_options.include = []
         runner_options.exclude = []
 
@@ -326,6 +327,8 @@
             'MAP *.test 127.0.0.1, MAP *.test. 127.0.0.1',
             *self.port.additional_driver_flags(),
         ])
+        if self.options.product == 'headless_shell':
+            runner_options.binary_args.append('--headless=old')
         # Implicitly pass `--enable-blink-features=MojoJS,MojoJSTest` to Chrome.
         runner_options.mojojs_path = self.port.generated_sources_directory()
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 20190b7..80a27013 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1592,6 +1592,23 @@
     "expires": "Sep 1, 2024"
   },
   {
+    "prefix": "fledge-deprecated-render-url-replacements",
+    "platforms": ["Linux", "Mac", "Win"],
+    "owners": ["ybourouphael@google.com", "behamilton@google.com", "mmenke@chromium.org", "pauljensen@chromium.org"],
+    "bases": [
+      "external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window.js",
+      "external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window.js",
+      "external/wpt/fledge/tentative/auction-config.https.window.js",
+      "external/wpt/fledge/tentative/component-ads.https.window.js",
+      "external/wpt/fledge/tentative/component-auction.https.window.js"
+    ],
+    "args": [
+      "--enable-features=InterestGroupStorage,PrivacySandboxAdsAPIsOverride,FencedFrames:implementation_type/mparch,FledgeConsiderKAnonymity",
+      "--disable-features=CookieDeprecationFacilitatedTesting"
+    ],
+    "expires": "Sep 1, 2024"
+  },
+  {
     "prefix": "automatic-lazy-frame-loading",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [
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 480a8daf..9c2f85ce 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
@@ -282734,6 +282734,32 @@
         {}
        ]
       ],
+      "paint-context-007.svg": [
+       "50360a302e3a3d2ca74b465122d322a25abf743a",
+       [
+        null,
+        [
+         [
+          "/svg/painting/reftests/paint-context-007-ref.svg",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "paint-context-008.svg": [
+       "83308f09b1fc95d538ee078ff9acbf7e7def307e",
+       [
+        null,
+        [
+         [
+          "/svg/painting/reftests/paint-context-008-ref.svg",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "paint-order-001.svg": [
        "c8d60bd5bf68d0dc5f56fb360acdd2bac5aa1aa2",
        [
@@ -346467,7 +346493,7 @@
       []
      ],
      "file-names.md": [
-      "96296c4ff69934a77581a50c5cf7299feadbb838",
+      "5336bfb528cec59ea3ce1fe78540470980969d0c",
       []
      ],
      "general-guidelines.md": [
@@ -346549,7 +346575,7 @@
       []
      ],
      "testdriver.md": [
-      "73af3bb3c8dd72fc283cdc9440fbe05bd768d1d3",
+      "fcf0199badc12e8cca1d017d266db4abe02544ad",
       []
      ],
      "testharness-api.md": [
@@ -384394,7 +384420,7 @@
      []
     ],
     "testdriver.js": [
-     "20140b2fc0a0b7c5bb32089b4e8283ffbb6e2a01",
+     "2d1a89690cc25f87e7d2762c3f5081081a902f7a",
      []
     ],
     "testdriver.js.headers": [
@@ -390895,6 +390921,14 @@
        "6aa42374c512b528868c55d0339eb4deedf09d69",
        []
       ],
+      "paint-context-007-ref.svg": [
+       "0328272c6aa0907e226cbbbc2008358bee43ef7b",
+       []
+      ],
+      "paint-context-008-ref.svg": [
+       "49d4f8947b003302a76888e6beea385a4019f404",
+       []
+      ],
       "paint-order-001-ref.svg": [
        "7822a80b942fee54a87106d7def13429454cc26e",
        []
@@ -504996,10 +505030,12 @@
      ]
     ],
     "setting-null-config-navigates-to-about-blank.https.html": [
-     "2595fd64c922b1e16220628fe1fa87fdf7c74f5f",
+     "c8322dab1996ccabc5ee980fd63bc52c57df9d05",
      [
       null,
-      {}
+      {
+       "timeout": "long"
+      }
      ]
     ],
     "shared-workers.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/docs/writing-tests/file-names.md b/third_party/blink/web_tests/external/wpt/docs/writing-tests/file-names.md
index 96296c4..5336bfb 100644
--- a/third_party/blink/web_tests/external/wpt/docs/writing-tests/file-names.md
+++ b/third_party/blink/web_tests/external/wpt/docs/writing-tests/file-names.md
@@ -11,10 +11,10 @@
 
 ### Test Type
 
-These flags must be the last element in the filename before the
-extension e.g. `foo-manual.html` will indicate a manual test, but
-`foo-manual-other.html` will not. Unlike test features, test types
-are mutually exclusive.
+These flags are preceded by a `-` and followed by a `.`, and must be the
+last such element in the filename, e.g. `foo-manual.html` will indicate
+a manual test, but `foo-manual-other.html` will not. Unlike test features,
+test types are mutually exclusive.
 
 
 `-manual`
@@ -26,8 +26,8 @@
 
 ### Test Features
 
-These flags are preceded by a `.` in the filename, and must
-themselves precede any test type flag, but are otherwise unordered.
+These flags are preceded and followed by a `.` in the filename, and must themselves
+go after any test type flag, but are otherwise unordered.
 
 
 `.https`
diff --git a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
index 73af3bb..fcf0199 100644
--- a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
+++ b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testdriver.md
@@ -119,6 +119,12 @@
 .. js:autofunction:: test_driver.get_virtual_sensor_information
 ```
 
+### Device Posture ###
+```eval_rst
+.. js:autofunction:: test_driver.set_device_posture
+.. js:autofunction:: test_driver.clear_device_posture
+```
+
 ### Using test_driver in other browsing contexts ###
 
 Testdriver can be used in browsing contexts (i.e. windows or frames)
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window.js
index 9b12d07..7780957 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window.js
@@ -81,6 +81,18 @@
 });
 
 makeTest({
+  name: 'AuctionConfig.deprecatedRenderURLReplacements with brackets.',
+  fieldName: 'deprecatedRenderURLReplacements',
+  fieldValue: {'${EXAMPLE_MACRO}': 'SSP'},
+});
+
+makeTest({
+  name: 'AuctionConfig.deprecatedRenderURLReplacements with percents.',
+  fieldName: 'deprecatedRenderURLReplacements',
+  fieldValue: {'%%EXAMPLE_MACRO%%': 'SSP'},
+});
+
+makeTest({
   name: 'AuctionConfig.seller is URL.',
   fieldName: 'seller',
   fieldValue: OTHER_ORIGIN1,
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window_1-5-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window_1-5-expected.txt
new file mode 100644
index 0000000..f77f9c25
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window_1-5-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] AuctionConfig.deprecatedRenderURLReplacements with brackets.
+  assert_true: Auction unexpectedly had no winner expected true got false
+[FAIL] AuctionConfig.deprecatedRenderURLReplacements with percents.
+  assert_true: Auction unexpectedly had no winner expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window.js
index 5fa4fa2..28ed147 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window.js
@@ -12,7 +12,8 @@
 // META: variant=?31-35
 // META: variant=?36-40
 // META: variant=?40-45
-// META: variant=?46-last
+// META: variant=?45-50
+// META: variant=?50-last
 
 "use strict;"
 
@@ -110,6 +111,62 @@
 }
 
 makeTest({
+  name: 'deprecatedRenderURLReplacements without end bracket is invalid.',
+  expect: EXPECT_PROMISE_ERROR,
+  expectPromiseError: EXPECT_EXCEPTION(TypeError),
+  auctionConfigOverrides: {deprecatedRenderURLReplacements: {'${No_End_Bracket': 'SSP'}}
+});
+
+makeTest({
+  name: 'deprecatedRenderURLReplacements without percents and brackets.',
+  expect: EXPECT_PROMISE_ERROR,
+  expectPromiseError: EXPECT_EXCEPTION(TypeError),
+  auctionConfigOverrides: {deprecatedRenderURLReplacements: {'No_Wrapper': 'SSP'}}
+});
+
+makeTest({
+  name: 'deprecatedRenderURLReplacements without dollar sign.',
+  expect: EXPECT_PROMISE_ERROR,
+  expectPromiseError: EXPECT_EXCEPTION(TypeError),
+  auctionConfigOverrides: {deprecatedRenderURLReplacements: {'{No_Dollar_Sign}': 'SSP'}}
+});
+
+makeTest({
+  name: 'deprecatedRenderURLReplacements without start bracket is invalid.',
+  expect: EXPECT_PROMISE_ERROR,
+  expectPromiseError: EXPECT_EXCEPTION(TypeError),
+  auctionConfigOverrides: {deprecatedRenderURLReplacements: {'$No_Start_Bracket}': 'SSP'}}
+});
+
+makeTest({
+  name: 'deprecatedRenderURLReplacements mix and match is invalid.',
+  expect: EXPECT_PROMISE_ERROR,
+  expectPromiseError: EXPECT_EXCEPTION(TypeError),
+  auctionConfigOverrides: {deprecatedRenderURLReplacements: {'${Bracket_And_Percent%%': 'SSP'}}
+});
+
+makeTest({
+  name: 'deprecatedRenderURLReplacements missing start percent is invalid.',
+  expect: EXPECT_PROMISE_ERROR,
+  expectPromiseError: EXPECT_EXCEPTION(TypeError),
+  auctionConfigOverrides: {deprecatedRenderURLReplacements: {'%Missing_Start_Percents%%': 'SSP'}}
+});
+
+makeTest({
+  name: 'deprecatedRenderURLReplacements single percents is invalid.',
+  expect: EXPECT_PROMISE_ERROR,
+  expectPromiseError: EXPECT_EXCEPTION(TypeError),
+  auctionConfigOverrides: {deprecatedRenderURLReplacements: {'%Single_Percents%': 'SSP'}}
+});
+
+makeTest({
+  name: 'deprecatedRenderURLReplacements without end percents is invalid.',
+  expect: EXPECT_PROMISE_ERROR,
+  expectPromiseError: EXPECT_EXCEPTION(TypeError),
+  auctionConfigOverrides: {deprecatedRenderURLReplacements: {'%%No_End_Percents': 'SSP'}}
+});
+
+makeTest({
   name: 'no buyers => no winners',
   expect: EXPECT_NO_WINNER,
   auctionConfigOverrides: {interestGroupBuyers: []},
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window_1-5-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window_1-5-expected.txt
new file mode 100644
index 0000000..4cc16150
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window_1-5-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+[FAIL] deprecatedRenderURLReplacements without end bracket is invalid.
+  assert_true: did not get expected error type: [object FencedFrameConfig] expected true got false
+[FAIL] deprecatedRenderURLReplacements without percents and brackets.
+  assert_true: did not get expected error type: [object FencedFrameConfig] expected true got false
+[FAIL] deprecatedRenderURLReplacements without dollar sign.
+  assert_true: did not get expected error type: [object FencedFrameConfig] expected true got false
+[FAIL] deprecatedRenderURLReplacements without start bracket is invalid.
+  assert_true: did not get expected error type: [object FencedFrameConfig] expected true got false
+[FAIL] deprecatedRenderURLReplacements mix and match is invalid.
+  assert_true: did not get expected error type: [object FencedFrameConfig] expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window_6-10-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window_6-10-expected.txt
new file mode 100644
index 0000000..f3d6faf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/auction-config.https.window_6-10-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] deprecatedRenderURLReplacements missing start percent is invalid.
+  assert_true: did not get expected error type: [object FencedFrameConfig] expected true got false
+[FAIL] deprecatedRenderURLReplacements single percents is invalid.
+  assert_true: did not get expected error type: [object FencedFrameConfig] expected true got false
+[FAIL] deprecatedRenderURLReplacements without end percents is invalid.
+  assert_true: did not get expected error type: [object FencedFrameConfig] expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/component-ads.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/component-ads.https.window.js
index 6b22585..8493025 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/component-ads.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/component-ads.https.window.js
@@ -44,13 +44,25 @@
 //
 // If "adMetadata" is true, metadata is added to each component ad. Only integer metadata
 // is used, relying on renderURL tests to cover other types of renderURL metadata.
+//
+// If "deprecatedRenderURLReplacements" is passed, the matches and replacements will be
+// used in the trackingURLs and the object will be passed into the auctionConfig, to
+// replace matching macros within the renderURLs.
 async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGroup,
-                                         componentAdsInBid, componentAdsToLoad,
-                                         adMetadata = false) {
+  componentAdsInBid, componentAdsToLoad,
+  adMetadata = false, deprecatedRenderURLReplacements = null) {
   let interestGroupAdComponents = [];
+  // These are used within the URLs for deprecatedRenderURLReplacement tests.
+  const renderURLReplacementsStrings = createStringBeforeAndAfterReplacements(deprecatedRenderURLReplacements);
+  const beforeReplacementsString= renderURLReplacementsStrings.beforeReplacements;
+  const afterReplacementsString = renderURLReplacementsStrings.afterReplacements;
+
   for (let i = 0; i < numComponentAdsInInterestGroup; ++i) {
-    const componentRenderURL = createComponentAdRenderURL(uuid, i);
-    let adComponent = {renderURL: componentRenderURL};
+    let componentRenderURL = createComponentAdRenderURL(uuid, i);
+    if (deprecatedRenderURLReplacements !== null) {
+      componentRenderURL = createTrackerURL(window.location.origin, uuid, 'track_get', beforeReplacementsString);
+    }
+    let adComponent = { renderURL: componentRenderURL };
     if (adMetadata)
       adComponent.metadata = i;
     interestGroupAdComponents.push(adComponent);
@@ -74,7 +86,7 @@
                                  eventData: status,
                                  destination: ["buyer"]});`);
 
-  let bid = {bid:1, render: renderURL};
+  let bid = {bid:1, render:renderURL};
   if (componentAdsInBid) {
     bid.adComponents = [];
     for (let index of componentAdsInBid) {
@@ -89,8 +101,13 @@
   // to "expectedTrackerURLs".
   if (componentAdsToLoad && bid.adComponents) {
     for (let index of componentAdsToLoad) {
+      let expectedURL = createComponentAdTrackerURL(uuid, componentAdsInBid[index]);
+      if (deprecatedRenderURLReplacements != null) {
+        expectedURL = createTrackerURL(window.location.origin, uuid, 'track_get',
+                                       afterReplacementsString);
+      }
       if (index < componentAdsInBid.length)
-        expectedTrackerURLs.push(createComponentAdTrackerURL(uuid, componentAdsInBid[index]));
+        expectedTrackerURLs.push(expectedURL);
     }
   }
 
@@ -127,11 +144,13 @@
       test, uuid,
       {decisionLogicURL: createDecisionScriptURL(
         uuid,
-        { scoreAd:
+          { scoreAd:
               `if (JSON.stringify(browserSignals.adComponents) !==
                        '${JSON.stringify(bid.adComponents)}') {
                  throw "Unexpected adComponents: " + JSON.stringify(browserSignals.adComponents);
-               }`})});
+               }`}),
+        deprecatedRenderURLReplacements: deprecatedRenderURLReplacements
+      });
   }
 
   await waitForObservedRequests(uuid, expectedTrackerURLs);
@@ -447,3 +466,27 @@
 
 
 }, 'Reports not sent from component ad.');
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/1,
+                                  /*componentAdsInBid=*/[0], /*componentAdsToLoad=*/[0], false, { '%%EXAMPLE-MACRO%%': 'SSP' });
+}, 'component ad with render url replacements with percents.');
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/1,
+                                  /*componentAdsInBid=*/[0], /*componentAdsToLoad=*/[0], false, { '${EXAMPLE-MACRO}': 'SSP' });
+}, 'component ad with render url replacements with brackets.');
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/1,
+                                  /*componentAdsInBid=*/[0], /*componentAdsToLoad=*/[0], false, { '${EXAMPLE-MACRO-1}': 'SSP-1', '%%EXAMPLE-MACRO-2%%': 'SSP-2' });
+}, 'component ad with render url replacements with multiple replacements.');
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+  await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/3,
+                                  /*componentAdsInBid=*/[0,1,2], /*componentAdsToLoad=*/[0,1,2], false, { '${EXAMPLE-MACRO-1}': 'SSP-1', '%%EXAMPLE-MACRO-2%%': 'SSP-2' });
+}, 'component ad with render url replacements with multiple replacements, and multiple component ads.');
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/component-ads.https.window_16-last-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/component-ads.https.window_16-last-expected.txt
new file mode 100644
index 0000000..bb41d8c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/component-ads.https.window_16-last-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+[FAIL] component ad with render url replacements with percents.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%EXAMPLE-MACRO%%/" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_post&id=bidder_beacon_1, body: ok"]
+[FAIL] component ad with render url replacements with brackets.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${EXAMPLE-MACRO}/" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_post&id=bidder_beacon_1, body: ok"]
+[FAIL] component ad with render url replacements with multiple replacements.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${EXAMPLE-MACRO-1}/%%EXAMPLE-MACRO-2%%/" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP-1/SSP-2/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_post&id=bidder_beacon_1, body: ok"]
+[FAIL] component ad with render url replacements with multiple replacements, and multiple component ads.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${EXAMPLE-MACRO-1}/%%EXAMPLE-MACRO-2%%/" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP-1/SSP-2/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP-1/SSP-2/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP-1/SSP-2/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_post&id=bidder_beacon_1, body: ok"]
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/component-auction.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/component-auction.https.window.js
index 015c20a5..bf804e6 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/component-auction.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/component-auction.https.window.js
@@ -11,18 +11,21 @@
 "use strict";
 
 // Creates an AuctionConfig with a single component auction.
-function createComponentAuctionConfig(uuid) {
+function createComponentAuctionConfig(uuid, auctionConfigOverrides = {},
+                                      deprecatedRenderURLReplacements = {}) {
   let componentAuctionConfig = {
     seller: window.location.origin,
     decisionLogicURL: createDecisionScriptURL(uuid),
-    interestGroupBuyers: [window.location.origin]
+    interestGroupBuyers: [window.location.origin],
+    deprecatedRenderURLReplacements: deprecatedRenderURLReplacements
   };
 
   return {
     seller: window.location.origin,
     decisionLogicURL: createDecisionScriptURL(uuid),
     interestGroupBuyers: [],
-    componentAuctions: [componentAuctionConfig]
+    componentAuctions: [componentAuctionConfig],
+    ...auctionConfigOverrides
   };
 }
 
@@ -717,3 +720,118 @@
       uuid,
       [bidderReportURL1, seller1ReportURL, bidderReportURL2, seller2ReportURL]);
 }, `Component auction prevWinsMs and numBids updating in one component seller's auction, read in another's.`);
+
+
+const makeDeprecatedRenderURLReplacementTest = ({
+  name,
+  deprecatedRenderURLReplacements,
+}) => {
+  subsetTest(promise_test, async test => {
+    const uuid = generateUuid(test);
+
+    let bidderReportURL = createBidderReportURL(uuid);
+    let componentSellerReportURL = createSellerReportURL(uuid, /*id=*/"component");
+    let topLevelSellerReportURL = createSellerReportURL(uuid, /*id=*/"top");
+
+    // These are used within the URLs for deprecatedRenderURLReplacement tests.
+    const renderURLReplacementsStrings = createStringBeforeAndAfterReplacements(deprecatedRenderURLReplacements);
+    const beforeReplacementsString = renderURLReplacementsStrings.beforeReplacements;
+    const afterReplacementsString = renderURLReplacementsStrings.afterReplacements;
+    const renderURLBeforeReplacements = createTrackerURL(window.location.origin, uuid, 'track_get', beforeReplacementsString);
+    const renderURLAfterReplacements = createTrackerURL(window.location.origin, uuid, 'track_get', afterReplacementsString);
+
+    await joinInterestGroup(
+      test, uuid,
+      {
+        ads: [{ renderURL: renderURLBeforeReplacements }],
+        biddingLogicURL: createBiddingScriptURL(
+          {
+            allowComponentAuction: true,
+            bid: 5,
+            reportWin:
+              `if (browserSignals.bid !== 5)
+                 throw "Unexpected bid: " + browserSignals.bid;
+               sendReportTo("${bidderReportURL}");`
+          })
+      });
+
+    let auctionConfig = createComponentAuctionConfig(uuid, {}, deprecatedRenderURLReplacements);
+
+    auctionConfig.componentAuctions[0].decisionLogicURL =
+      createDecisionScriptURL(
+        uuid,
+        {
+          scoreAd:
+            `if (bid !== 5)
+                   throw "Unexpected component bid: " + bid`,
+          reportResult:
+            `if (browserSignals.bid !== 5)
+                   throw "Unexpected component bid: " + browserSignals.bid;
+                 if (browserSignals.modifiedBid !== undefined)
+                   throw "Unexpected component modifiedBid: " + browserSignals.modifiedBid;
+                 sendReportTo("${componentSellerReportURL}");`
+        });
+
+    auctionConfig.decisionLogicURL =
+      createDecisionScriptURL(
+        uuid,
+        {
+          scoreAd:
+            `if (bid !== 5)
+                   throw "Unexpected top-level bid: " + bid`,
+          reportResult:
+            `if (browserSignals.bid !== 5)
+                   throw "Unexpected top-level bid: " + browserSignals.bid;
+                 if (browserSignals.modifiedBid !== undefined)
+                   throw "Unexpected top-level modifiedBid: " + browserSignals.modifiedBid;
+                 sendReportTo("${topLevelSellerReportURL}");`
+        });
+
+    await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfig);
+    await waitForObservedRequests(
+      uuid,
+      [bidderReportURL, componentSellerReportURL, topLevelSellerReportURL, renderURLAfterReplacements]);
+  }, name);
+};
+
+makeDeprecatedRenderURLReplacementTest({
+  name: 'Replacements with brackets.',
+  deprecatedRenderURLReplacements: { '${EXAMPLE-MACRO}': 'SSP' }
+});
+
+makeDeprecatedRenderURLReplacementTest({
+  name: 'Replacements with percents.',
+  deprecatedRenderURLReplacements: { '%%EXAMPLE-MACRO%%': 'SSP' }
+});
+
+makeDeprecatedRenderURLReplacementTest({
+  name: 'Replacements with multiple replacements.',
+  deprecatedRenderURLReplacements: { '${EXAMPLE-MACRO1}': 'SSP1', '%%EXAMPLE-MACRO2%%': 'SSP2' }
+});
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+  let deprecatedRenderURLReplacements = { '${EXAMPLE-MACRO1}': 'SSP1', '%%EXAMPLE-MACRO2%%': 'SSP2' };
+  const renderURLReplacementsStrings = createStringBeforeAndAfterReplacements(deprecatedRenderURLReplacements);
+  let beforeReplacementsString = renderURLReplacementsStrings.beforeReplacements;
+
+  await joinInterestGroup(
+    test, uuid,
+    {
+      ads: [{ renderURL: createTrackerURL(window.location.origin, uuid, 'track_get', beforeReplacementsString) }],
+      biddingLogicURL: createBiddingScriptURL({allowComponentAuction: true})
+    });
+  let auctionConfigOverride = {deprecatedRenderURLReplacements: deprecatedRenderURLReplacements }
+  let auctionConfig = createComponentAuctionConfig(uuid,/*auctionConfigOverride=*/auctionConfigOverride,
+  /*deprecatedRenderURLReplacements=*/deprecatedRenderURLReplacements);
+
+  auctionConfig.componentAuctions[0].decisionLogicURL = createDecisionScriptURL(uuid);
+
+    try {
+      await runBasicFledgeAuction(test, uuid, auctionConfig);
+    } catch (exception) {
+      assert_true(exception instanceof TypeError, "did not get expected error: " + exception);
+      return;
+    }
+    throw 'Exception unexpectedly not thrown.'
+}, "deprecatedRenderURLReplacements cause error if passed in top level auction and component auction.");
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/component-auction.https.window_16-last-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/component-auction.https.window_16-last-expected.txt
new file mode 100644
index 0000000..afdb5ea
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/component-auction.https.window_16-last-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+[FAIL] Replacements with brackets.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${EXAMPLE-MACRO}/" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_component", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_top"]
+[FAIL] Replacements with percents.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%EXAMPLE-MACRO%%/" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_component", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_top"]
+[FAIL] Replacements with multiple replacements.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${EXAMPLE-MACRO1}/%%EXAMPLE-MACRO2%%/" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP1/SSP2/", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_component", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_top"]
+[FAIL] deprecatedRenderURLReplacements cause error if passed in top level auction and component auction.
+  promise_test: Unhandled rejection with value: "Exception unexpectedly not thrown."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window.js
new file mode 100644
index 0000000..4f8bc1c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window.js
@@ -0,0 +1,196 @@
+// META: script=/resources/testdriver.js
+// META: script=/common/utils.js
+// META: script=resources/fledge-util.sub.js
+// META: script=/common/subset-tests.js
+// META: timeout=long
+// META: variant=?1-5
+// META: variant=?6-10
+// META: variant=?11-15
+// META: variant=?16-last
+
+
+"use strict;"
+
+// This test ensures proper handling of deprecatedRenderURLReplacements within auctionConfigOverrides.
+// It validates that these replacements are correctly applied to the winning bid's renderURL by
+// injecting a URL with matching macros into an interest group and ensuring that a new url with
+// the replacements in it, is tracked and observed.
+const makeTest = ({
+    // Test name
+    name,
+    // Overrides to the interest group.
+    interestGroupOverrides = {},
+    // Overrides to the auction config.
+    auctionConfigOverrides = {},
+    // This is what goes into the renderURL and is expected to be replaced.
+    beforeReplacements,
+    // This is what's expected when 'beforeReplacements' is replaced.
+    afterReplacements,
+}) => {
+    subsetTest(promise_test, async test => {
+        const uuid = generateUuid(test);
+        let urlBeforeReplacements = createTrackerURL(window.location.origin, uuid, 'track_get', beforeReplacements);
+        let urlAfterReplacements = createTrackerURL(window.location.origin, uuid, 'track_get', afterReplacements);
+        interestGroupOverrides.ads = [{ renderURL: urlBeforeReplacements }];
+        await joinInterestGroup(test, uuid, interestGroupOverrides);
+
+        await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
+        await waitForObservedRequests(
+            uuid,
+            [urlAfterReplacements, createSellerReportURL(uuid), createBidderReportURL(uuid)]);
+    }, name);
+};
+
+makeTest({
+    name: 'Replacements with brackets.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${EXAMPLE-MACRO}': 'SSP' }
+    },
+    beforeReplacements: "${EXAMPLE-MACRO}",
+    afterReplacements: 'SSP',
+
+});
+
+makeTest({
+    name: 'Replacements with percents.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '%%EXAMPLE-MACRO%%': 'SSP' }
+    },
+    beforeReplacements: "%%EXAMPLE-MACRO%%",
+    afterReplacements: 'SSP',
+});
+
+makeTest({
+    name: 'Multiple replacements within a URL.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${EXAMPLE-MACRO1}': 'SSP1', '%%EXAMPLE-MACRO2%%': 'SSP2' }
+    },
+    beforeReplacements: "${EXAMPLE-MACRO1}/%%EXAMPLE-MACRO2%%",
+    afterReplacements: 'SSP1/SSP2',
+});
+
+makeTest({
+    name: 'Recursive and reduce size with brackets.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${1}': '1' }
+    },
+    beforeReplacements: "${${${1}}}",
+    afterReplacements: "${${1}}"
+});
+
+makeTest({
+    name: 'Recursive and increase size with brackets.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${1}': '${${1}}' }
+    },
+    beforeReplacements: "${1}",
+    afterReplacements: "${${1}}"
+});
+
+makeTest({
+    name: 'Replacements use a single pass with brackets.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${1}': '${2}', '${2}': '${1}' }
+    },
+    beforeReplacements: "${1}${2}",
+    afterReplacements: "${2}${1}"
+});
+
+makeTest({
+    name: 'Multiple instances of same substitution string with brackets.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${1}': '${2}' }
+    },
+    beforeReplacements: "{${1}${1}}",
+    afterReplacements: "{${2}${2}}"
+});
+
+makeTest({
+    name: 'Mismatched replacement with brackets.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${2}': '${1}' }
+    },
+    beforeReplacements: "${1}",
+    afterReplacements: "${1}"
+});
+
+makeTest({
+    name: 'Recursive and reduce size with percents.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '%%1%%': '1' }
+    },
+    beforeReplacements: "%%%%1%%%%",
+    afterReplacements: "%%1%%"
+});
+
+makeTest({
+    name: 'Recursive and increase size with percents.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '%%1%%': '%%%%1%%%%' }
+    },
+    beforeReplacements: "%%1%%",
+    afterReplacements: "%%%%1%%%%"
+});
+
+makeTest({
+    name: 'Replacements use a single pass with percents.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '%%1%%': '%%2%%', '%%2%%': '%%1%%' }
+    },
+    beforeReplacements: "%%1%%%%2%%",
+    afterReplacements: "%%2%%%%1%%"
+});
+
+makeTest({
+    name: 'Multiple instances of same substitution string with percents.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '%%1%%': '%%2%%' }
+    },
+    beforeReplacements: "%%1%%%%1%%",
+    afterReplacements: "%%2%%%%2%%"
+});
+
+makeTest({
+    name: 'Mismatched replacement with percents.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '%%2%%': '%%1%%' }
+    },
+    beforeReplacements: "%%1%%",
+    afterReplacements: "%%1%%"
+});
+
+makeTest({
+    name: 'Case sensativity.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '%%foo%%': '%%bar%%' }
+    },
+    beforeReplacements: "%%FOO%%%%foo%%",
+    afterReplacements: "%%FOO%%%%bar%%"
+});
+
+makeTest({
+    name: 'Super macro, a macro with a macro inside it basically, with percents.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '%%%%foo%%%%': 'foo' }
+    },
+    beforeReplacements: "%%%%foo%%%%",
+    afterReplacements: "foo"
+});
+
+makeTest({
+    name: 'Super macro, with brackets.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${${foo}}': 'foo' }
+    },
+    beforeReplacements: "${${foo}}",
+    afterReplacements: "foo"
+});
+
+makeTest({
+    name: 'Super macro, with both.',
+    auctionConfigOverrides: {
+        deprecatedRenderURLReplacements: { '${%%foo%%}': 'foo', '%%${bar}%%':'bar' }
+    },
+    beforeReplacements: "${%%foo%%}%%${bar}%%",
+    afterReplacements: "foobar"
+});
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_1-5-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_1-5-expected.txt
new file mode 100644
index 0000000..7f0d1628
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_1-5-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+[FAIL] Replacements with brackets.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${EXAMPLE-MACRO}" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Replacements with percents.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%EXAMPLE-MACRO%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Multiple replacements within a URL.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${EXAMPLE-MACRO1}/%%EXAMPLE-MACRO2%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=SSP1/SSP2", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Recursive and reduce size with brackets.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${${${1}}}" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${${1}}", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Recursive and increase size with brackets.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${1}" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${${1}}", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_11-15-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_11-15-expected.txt
new file mode 100644
index 0000000..661d001
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_11-15-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+[FAIL] Replacements use a single pass with percents.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%1%%%%2%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%2%%%%1%%", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Multiple instances of same substitution string with percents.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%1%%%%1%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%2%%%%2%%", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Case sensativity.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%FOO%%%%foo%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%FOO%%%%bar%%", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Super macro, a macro with a macro inside it basically, with percents.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%%%foo%%%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=foo", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_16-last-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_16-last-expected.txt
new file mode 100644
index 0000000..ccdfedec
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_16-last-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] Super macro, with brackets.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${${foo}}" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=foo", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Super macro, with both.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${%%foo%%}%%${bar}%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=foobar", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_6-10-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_6-10-expected.txt
new file mode 100644
index 0000000..8462c72
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_6-10-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+[FAIL] Replacements use a single pass with brackets.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${1}${2}" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=${2}${1}", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Multiple instances of same substitution string with brackets.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id={${1}${1}}" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id={${2}${2}}"]
+[FAIL] Recursive and reduce size with percents.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%%%1%%%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%1%%", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+[FAIL] Recursive and increase size with percents.
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%1%%" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=%%%%1%%%%", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1", "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=seller_report_1"]
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/kanon-status-below-threshold.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/fledge/tentative/kanon-status-below-threshold.https.window-expected.txt
index 89e64d0..e27cfa4 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/kanon-status-below-threshold.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/kanon-status-below-threshold.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
 [FAIL] Check kAnonStatus is "belowThreshold" when FledgeConsiderKAnonymityis enabled and FledgeEnforceKAnonymity is disabled
-  assert_array_equals: expected property 0 to be "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1" but got "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_error" (expected array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1"] got ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_error"])
+  assert_in_array: value "https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_error" not in array ["https://web-platform.test:8444/fledge/tentative/resources/request-tracker.py?uuid=<uuid>&dispatch=track_get&id=bidder_report_1"]
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js
index 32162e93..a7d0f63 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js
@@ -151,19 +151,21 @@
       trackedRequests = trackedRequests.filter(filter);
     }
 
-    // If expected number of requests have been observed, compare with list of
-    // all expected requests and exit.
-    if (trackedRequests.length >= expectedRequests.length) {
-      assert_array_equals(trackedRequests, expectedRequests);
-      break;
-    }
-
     // If fewer than total number of expected requests have been observed,
     // compare what's been received so far, to have a greater chance to fail
     // rather than hang on error.
     for (const trackedRequest of trackedRequests) {
       assert_in_array(trackedRequest, expectedRequests);
     }
+
+    // If expected number of requests have been observed, compare with list of
+    // all expected requests and exit. This check was previously before the for loop,
+    // but was swapped in order to avoid flakiness with failing tests and their
+    // respective *-expected.txt.
+    if (trackedRequests.length >= expectedRequests.length) {
+      assert_array_equals(trackedRequests, expectedRequests);
+      break;
+    }
   }
 }
 
@@ -839,3 +841,21 @@
     fetchAdditionalBids: fetchAdditionalBids
   };
 }();
+
+
+// DeprecatedRenderURLReplacements helper function.
+// Returns an object containing sample strings both before and after the
+// replacements in 'replacements' have been applied by
+// deprecatedRenderURLReplacements. All substitution strings will appear
+// only once in the output strings.
+function createStringBeforeAndAfterReplacements(deprecatedRenderURLReplacements) {
+  let beforeReplacements = '';
+  let afterReplacements = '';
+  if(deprecatedRenderURLReplacements){
+    for (const [match, replacement] of Object.entries(deprecatedRenderURLReplacements)) {
+      beforeReplacements += match + "/";
+      afterReplacements += replacement + "/";
+    }
+  }
+  return { beforeReplacements, afterReplacements };
+}
diff --git a/third_party/blink/web_tests/external/wpt/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html b/third_party/blink/web_tests/external/wpt/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html
index 35844bc9..1b84b43f 100644
--- a/third_party/blink/web_tests/external/wpt/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html
+++ b/third_party/blink/web_tests/external/wpt/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html
@@ -20,36 +20,32 @@
 
     const innerIFrame = outerIFrame.contentDocument.createElement("iframe");
 
-    window.onmessage = function() {
+    window.onmessage = function(event) {
+      if (event.data != "ready") {
+        return;
+      }
+
+      // We receive an message when the innerIFrame is ready and its input is focused.
+      // outerIframe is the ancestor of inner iframe, so the activeElement of
+      // it should be the inner iframe.
+      assert_equals(outerIFrame.contentDocument.activeElement, innerIFrame,
+        "The activeElement of the outer iframe should be the inner iframe");
+
+      // Now we focus the input in the top level
+      document.querySelector("input").focus();
+
       // Wait for a bit to let whatever the code that might change the focus to run
       window.requestAnimationFrame(function() {
         window.requestAnimationFrame(function() {
           window.requestAnimationFrame(function() {
-
-            // We receive an message when the innerIFrame is ready and its input is focused.
-            // outerIframe is the ancestor of inner iframe, so the activeElement of
-            // it should be the inner iframe.
-            assert_equals(outerIFrame.contentDocument.activeElement, innerIFrame,
-              "The activeElement of the outer iframe should be the inner iframe");
-
-            // Now we focus the input in the top level
-            document.querySelector("input").focus();
-
-            // Wait for a bit to let whatever the code that might change the focus to run
-            window.requestAnimationFrame(function() {
-              window.requestAnimationFrame(function() {
-                window.requestAnimationFrame(function() {
-                  // Since inner iframe lost its focus, the activeElement of outer iframe
-                  // should be cleared as well, hence <body> should be focused.
-                  assert_equals(outerIFrame.contentDocument.activeElement, outerIFrame.contentDocument.body,
-                    "The activeElement of the outer iframe should be reverted back to <body>");
-                  assert_equals(document.activeElement, document.querySelector("input"),
-                    "The activeElement of the top-level document should the input");
-                  done();
-                });
-              });
-            });
-          });
+            // Since inner iframe lost its focus, the activeElement of outer iframe
+            // should be cleared as well, hence <body> should be focused.
+            assert_equals(outerIFrame.contentDocument.activeElement, outerIFrame.contentDocument.body,
+              "The activeElement of the outer iframe should be reverted back to <body>");
+            assert_equals(document.activeElement, document.querySelector("input"),
+              "The activeElement of the top-level document should the input");
+            done();
+          })
         });
       });
     }
diff --git a/third_party/blink/web_tests/external/wpt/focus/support/cross-origin-ancestor-activeelement-after-child-lose-focus-helper.html b/third_party/blink/web_tests/external/wpt/focus/support/cross-origin-ancestor-activeelement-after-child-lose-focus-helper.html
index 83d39d5..fe966145 100644
--- a/third_party/blink/web_tests/external/wpt/focus/support/cross-origin-ancestor-activeelement-after-child-lose-focus-helper.html
+++ b/third_party/blink/web_tests/external/wpt/focus/support/cross-origin-ancestor-activeelement-after-child-lose-focus-helper.html
@@ -2,8 +2,10 @@
 <body>
   <input />
   <script>
-    document.querySelector("input").focus();
-    window.parent.parent.postMessage("ready", '*');
+    window.onload = function() {
+      document.querySelector("input").focus();
+      window.parent.parent.postMessage("ready", '*');
+    }
   </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/resources/testdriver.js b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
index 20140b2..2d1a8969 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testdriver.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
@@ -1023,6 +1023,49 @@
          */
         get_virtual_sensor_information: function(sensor_type, context=null) {
             return window.test_driver_internal.get_virtual_sensor_information(sensor_type, context);
+        },
+
+        /**
+         * Overrides device posture set by hardware.
+         *
+         * Matches the `Set device posture
+         * <https://w3c.github.io/device-posture/#set-device-posture>`_
+         * WebDriver command.
+         *
+         * @param {String} posture - A `DevicePostureType
+         *                           <https://w3c.github.io/device-posture/#dom-deviceposturetype>`_
+         *                           either "continuous" or "folded".
+         * @param {WindowProxy} [context=null] - Browsing context in which to
+         *                                       run the call, or null for the
+         *                                       current browsing context.
+         *
+         * @returns {Promise} Fulfilled when device posture is set.
+         *                    Rejected in case the WebDriver command errors out
+         *                    (including if a device posture of the given type
+         *                    does not exist).
+         */
+        set_device_posture: function(posture, context=null) {
+            return window.test_driver_internal.set_device_posture(posture, context);
+        },
+
+        /**
+         * Removes device posture override and returns device posture control
+         * back to hardware.
+         *
+         * Matches the `Clear device posture
+         * <https://w3c.github.io/device-posture/#clear-device-posture>`_
+         * WebDriver command.
+         *
+         * @param {WindowProxy} [context=null] - Browsing context in which to
+         *                                       run the call, or null for the
+         *                                       current browsing context.
+         *
+         * @returns {Promise} Fulfilled after the device posture override has
+         *                    been removed. Rejected in case the WebDriver
+         *                    command errors out.
+         */
+        clear_device_posture: function(context=null) {
+            return window.test_driver_internal.clear_device_posture(context);
         }
     };
 
@@ -1203,6 +1246,14 @@
 
         async get_virtual_sensor_information(sensor_type, context=null) {
             throw new Error("get_virtual_sensor_information() is not implemented by testdriver-vendor.js");
+        },
+
+        async set_device_posture(posture, context=null) {
+            throw new Error("set_device_posture() is not implemented by testdriver-vendor.js");
+        },
+
+        async clear_device_posture(context=null) {
+            throw new Error("clear_device_posture() is not implemented by testdriver-vendor.js");
         }
     };
 })();
diff --git a/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/README.md b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/README.md
new file mode 100644
index 0000000..fb0975f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/README.md
@@ -0,0 +1,5 @@
+This directory is to test deprecatedRenderURLReplacements within Protected Audience with FLEDGE (https://github.com/WICG/turtledove/blob/main/FLEDGE.md) on.
+
+If the `CookieDeprecationFacilitatedTesting` flag is on, we turn off `deprecatedRenderURLReplacements`.
+
+This virtual test is needed to disable `CookieDeprecationFacilitatedTesting` but allow for FLEDGE to be enabled. This way we will properly be able to test `deprecatedRenderURLReplacements`.
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window_1-5-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window_1-5-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config-passed-to-worklets.https.window_1-5-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-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config.https.window_1-5-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config.https.window_1-5-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config.https.window_1-5-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-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config.https.window_6-10-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config.https.window_6-10-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/auction-config.https.window_6-10-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-deprecated-render-url-replacements/external/wpt/fledge/tentative/component-ads.https.window_16-last-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/component-ads.https.window_16-last-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/component-ads.https.window_16-last-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-deprecated-render-url-replacements/external/wpt/fledge/tentative/component-auction.https.window_16-last-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/component-auction.https.window_16-last-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/component-auction.https.window_16-last-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-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_1-5-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_1-5-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_1-5-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-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_11-15-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_11-15-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_11-15-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-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_16-last-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_16-last-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_16-last-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-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_6-10-expected.txt b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_6-10-expected.txt
new file mode 100644
index 0000000..d2490db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/fledge-deprecated-render-url-replacements/external/wpt/fledge/tentative/deprecated-render-url-replacements.https.window_6-10-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/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index bcc52e4b..18ab9a7 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2936,11 +2936,13 @@
     getter longitude
     getter speed
     method constructor
+    method toJSON
 interface GeolocationPosition
     attribute @@toStringTag
     getter coords
     getter timestamp
     method constructor
+    method toJSON
 interface GeolocationPositionError
     attribute @@toStringTag
     attribute PERMISSION_DENIED
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 3397c6d..83481d7d 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -3450,11 +3450,13 @@
     getter longitude
     getter speed
     method constructor
+    method toJSON
 interface GeolocationPosition
     attribute @@toStringTag
     getter coords
     getter timestamp
     method constructor
+    method toJSON
 interface GeolocationPositionError
     attribute @@toStringTag
     attribute PERMISSION_DENIED
diff --git a/third_party/blink/web_tests/wpt_internal/geolocation-api/json.https.html b/third_party/blink/web_tests/wpt_internal/geolocation-api/json.https.html
new file mode 100644
index 0000000..67711f33
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/geolocation-api/json.https.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/testharness-adapter.js"></script>
+</head>
+<body>
+<script type="module">
+import {GeolocationMock} from './resources/geolocation-mock.js';
+
+async_test((t) => {
+  const mockCoords = {accuracy: 100,
+                      latitude: 51.478,
+                      longitude: -0.166,
+                      altitude: null,
+                      altitudeAccuracy: null,
+                      heading: null,
+                      speed: null};
+
+  const mock = new GeolocationMock();
+  mock.setGeolocationPermission(true);
+  mock.setGeolocationPosition(mockCoords.latitude,
+                              mockCoords.longitude,
+                              mockCoords.accuracy);
+
+  navigator.geolocation.getCurrentPosition(t.step_func_done((position) => {
+    assert_object_equals(position.coords.toJSON(), mockCoords);
+
+    const timestamp = position.timestamp;
+    const expectedPosition = {timestamp, coords: mockCoords};
+    assert_object_equals(position.toJSON(), expectedPosition);
+  }), t.step_func_done((e) => {
+    assert_unreached('Error callback invoked unexpectedly');
+  }));
+}, "Tests toJSON() on GeolocationPosition and GeolocationCoordinates.");
+</script>
+</body>
+</html>
diff --git a/third_party/catapult b/third_party/catapult
index 7d44c80..e939ac7 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 7d44c8067842719b399fb10f625e573e29cb723b
+Subproject commit e939ac77bb9471acc10f49e82cfe65790068c3d1
diff --git a/third_party/chromite b/third_party/chromite
index edee6b1..8af9063 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit edee6b1f898f39a0e19e99d28be674a1aaa546a3
+Subproject commit 8af906338f6c07813067abf296170c8a4f46c538
diff --git a/third_party/depot_tools b/third_party/depot_tools
index ed3d513..af97284 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit ed3d513241532dbea8e324ec9e1fe767bd75bddf
+Subproject commit af97284b58afb6bdb7dd2b353bc651c718ce5bb4
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index 904930d..84f6810 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit 904930d0bf12b7030892a118a2d2a6817dde61a3
+Subproject commit 84f68106afc210db30fb1efa8f76425cb067e82b
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 7c7726e..42135e49 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-13-2-113-gd091bca54
-Revision: d091bca546fa15928db36c8447e126ee43ddb5f4
+Version: VER-2-13-2-114-g12adfc212
+Revision: 12adfc212bd2f7560e1e175e66458124f9bd554b
 CPEPrefix: cpe:/a:freetype:freetype:2.13.2
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/freetype/src b/third_party/freetype/src
index d091bca..12adfc2 160000
--- a/third_party/freetype/src
+++ b/third_party/freetype/src
@@ -1 +1 @@
-Subproject commit d091bca546fa15928db36c8447e126ee43ddb5f4
+Subproject commit 12adfc212bd2f7560e1e175e66458124f9bd554b
diff --git a/third_party/openscreen/src b/third_party/openscreen/src
index 624f91b..dcd61df 160000
--- a/third_party/openscreen/src
+++ b/third_party/openscreen/src
@@ -1 +1 @@
-Subproject commit 624f91b189a5a6fd35b1d3c43c60678c1f111dff
+Subproject commit dcd61dfe0e1e6c27d6d48fd4a29a9117e7d4b666
diff --git a/third_party/perfetto b/third_party/perfetto
index f6656e9..234fd02 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit f6656e9828f5a5a5358d0782bd4d5cdecdc652f5
+Subproject commit 234fd02711c642ec5211da272283db8fd8d91af4
diff --git a/third_party/skia b/third_party/skia
index 2790777..a3a0165 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 2790777048d3f00384f68be8081f3a3db615746c
+Subproject commit a3a016537a8c512df42b50522e78710b320c0faf
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 01c82a0..6066c0d 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 01c82a000dcec3cc0452ef2faee80167fd788b79
+Subproject commit 6066c0d57a8bd7d68060336c7b01eeb9a1271589
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index 11eade5..0da8f2f 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit 11eade521a44d63af95cc72cfbbfa6c6becf972f
+Subproject commit 0da8f2f1189d05814b5bbfd770f362928f2fb829
diff --git a/third_party/webgpu-cts/ts_sources.txt b/third_party/webgpu-cts/ts_sources.txt
index 141cba8..2ecce13 100644
--- a/third_party/webgpu-cts/ts_sources.txt
+++ b/third_party/webgpu-cts/ts_sources.txt
@@ -824,6 +824,12 @@
 src/webgpu/shader/validation/expression/call/builtin/unpack4xI8.spec.ts
 src/webgpu/shader/validation/expression/call/builtin/unpack4xU8.spec.ts
 src/webgpu/shader/validation/expression/call/builtin/workgroupUniformLoad.spec.ts
+src/webgpu/shader/validation/expression/matrix/add_sub.spec.ts
+src/webgpu/shader/validation/expression/matrix/and_or_xor.spec.ts
+src/webgpu/shader/validation/expression/matrix/bitwise_shift.spec.ts
+src/webgpu/shader/validation/expression/matrix/comparison.spec.ts
+src/webgpu/shader/validation/expression/matrix/div_rem.spec.ts
+src/webgpu/shader/validation/expression/matrix/mul.spec.ts
 src/webgpu/shader/validation/expression/unary/address_of_and_indirection.spec.ts
 src/webgpu/shader/validation/expression/unary/arithmetic_negation.spec.ts
 src/webgpu/shader/validation/expression/unary/bitwise_complement.spec.ts
@@ -879,6 +885,7 @@
 src/webgpu/shader/validation/shader_io/locations.spec.ts
 src/webgpu/shader/validation/shader_io/size.spec.ts
 src/webgpu/shader/validation/shader_io/workgroup_size.spec.ts
+src/webgpu/shader/validation/statement/break_if.spec.ts
 src/webgpu/shader/validation/statement/for.spec.ts
 src/webgpu/shader/validation/statement/if.spec.ts
 src/webgpu/shader/validation/statement/increment_decrement.spec.ts
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4abf9551..e559d5f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2700,18 +2700,6 @@
   <int value="3" label="Dismissed"/>
 </enum>
 
-<enum name="CampaignButtonId">
-  <int value="0" label="Primary button"/>
-  <int value="1" label="Secondary button"/>
-</enum>
-
-<enum name="CampaignSlot">
-  <int value="0" label="Demo Mode App"/>
-  <int value="1" label="Demo Mode Free Play Apps"/>
-  <int value="2" label="Nudge"/>
-  <int value="3" label="Notification"/>
-</enum>
-
 <enum name="CanaryCheckLookupResult">
   <int value="0" label="Success"/>
   <int value="1" label="Failure"/>
diff --git a/tools/metrics/histograms/histograms_xml_files.gni b/tools/metrics/histograms/histograms_xml_files.gni
index 2ab6cf7..dcc7c03 100644
--- a/tools/metrics/histograms/histograms_xml_files.gni
+++ b/tools/metrics/histograms/histograms_xml_files.gni
@@ -14,6 +14,8 @@
   "//tools/metrics/histograms/metadata/ash/histograms.xml",
   "//tools/metrics/histograms/metadata/ash_clipboard/enums.xml",
   "//tools/metrics/histograms/metadata/ash_clipboard/histograms.xml",
+  "//tools/metrics/histograms/metadata/ash_growth/enums.xml",
+  "//tools/metrics/histograms/metadata/ash_growth/histograms.xml",
   "//tools/metrics/histograms/metadata/ash_user_education/enums.xml",
   "//tools/metrics/histograms/metadata/ash_user_education/histograms.xml",
   "//tools/metrics/histograms/metadata/assistant/histograms.xml",
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 265ff55..fa6649aa 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -2146,7 +2146,7 @@
 </histogram>
 
 <histogram name="Android.Messages.Error.FullyVisibleNotInformed"
-    enum="MessageIdentifier" expires_after="2024-05-19">
+    enum="MessageIdentifier" expires_after="2024-09-29">
   <owner>lazzzis@chromium.org</owner>
   <owner>src/components/messages/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index ed59586..20db0bd 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -202,12 +202,6 @@
                install should be very quick and always succeed."/>
 </variants>
 
-<variants name="CampaignButtonId">
-  <variant name="0" summary="Primary"/>
-  <variant name="1" summary="Secondary"/>
-  <variant name="2" summary="Others"/>
-</variants>
-
 <variants name="DisplayModes">
 <!-- Should be kept in sync with variants AppDisplayModes in
   tools/metrics/histograms/metadata/apps/histograms.xml.
@@ -4193,105 +4187,6 @@
   </token>
 </histogram>
 
-<histogram name="Ash.Growth.CampaignsComponent.DownloadDurationInOobe"
-    units="ms" expires_after="2024-10-17">
-  <owner>llin@google.com</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    The amount of time in ms that the Campaigns Manager takes to download the
-    campaigns component from Omaha. Recorded when a component loading complete
-    in the OOBE flow. This metric is splitted from
-    Ash.Growth.CampaignsComponent.DownloadDuration in M124.
-  </summary>
-</histogram>
-
-<histogram name="Ash.Growth.CampaignsComponent.DownloadDurationSessionStart"
-    units="ms" expires_after="2024-10-17">
-  <owner>llin@google.com</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    The amount of time in ms that the Campaigns Manager takes to download the
-    campaigns component from Omaha. Recorded when a component loading complete
-    at session start. This metric is splitted from
-    Ash.Growth.CampaignsComponent.DownloadDuration in M124.
-  </summary>
-</histogram>
-
-<histogram name="Ash.Growth.CampaignsComponent.ParseDuration" units="ms"
-    expires_after="2024-10-17">
-  <owner>llin@google.com</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    The amount of time in ms that the Campaigns Manager takes to read and parsed
-    (as JSON) from disk. Recorded when a component is loaded into Campaign
-    Matcher.
-  </summary>
-</histogram>
-
-<histogram name="Ash.Growth.CampaignsManager.Error"
-    enum="CampaignsManagerError" expires_after="2024-10-17">
-  <owner>llin@google.com</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    Tracks unexpected error while loading and matching campaigns, recorded each
-    time an unexpected error occurs.
-  </summary>
-</histogram>
-
-<histogram name="Ash.Growth.CampaignsManager.GetCampaignBySlot"
-    enum="CampaignSlot" expires_after="2024-10-17">
-  <owner>llin@google.com</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    Tracks the slot of the fetched campaign, recorded each time a campaign is
-    fetched.
-  </summary>
-</histogram>
-
-<histogram name="Ash.Growth.CampaignsManager.MatchDuration" units="ms"
-    expires_after="2024-10-17">
-  <owner>llin@google.com</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    The amount of time in ms that the Campaigns Manager takes to match a
-    campaign based on the targeting criteria. Recorded when fetching a campaign
-    for a particular slot completed.
-  </summary>
-</histogram>
-
-<histogram name="Ash.Growth.Ui.ButtonPressed.Button{ButtonId}.Campaigns500"
-    units="int" expires_after="2025-03-01">
-  <owner>wutao@chromium.org</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    Recorded when the button of {ButtonId} in the UI is pressed. The bucket is
-    indexed by the campaign_id. The campaign_id recorded in this histogram
-    should be less than 500.
-  </summary>
-  <token key="ButtonId" variants="CampaignButtonId"/>
-</histogram>
-
-<histogram name="Ash.Growth.Ui.Dismissed.Campaigns500" units="int"
-    expires_after="2025-03-01">
-  <owner>wutao@chromium.org</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    Recorded when the UI is dismissed. The bucket is indexed by the campaign_id.
-    The campaign_id recorded in this histogram should be less than 500.
-  </summary>
-</histogram>
-
-<histogram name="Ash.Growth.Ui.Impression.Campaigns500" units="int"
-    expires_after="2025-03-01">
-  <owner>wutao@chromium.org</owner>
-  <owner>cros-growth@google.com</owner>
-  <summary>
-    Recorded when it is ready to log a UI impression. Normally this is called
-    right after a call to show the UI. The bucket is indexed by the campaign_id.
-    The campaign_id recorded in this histogram should be less than 500.
-  </summary>
-</histogram>
-
 <histogram name="Ash.Homescreen.AnimationSmoothness" units="%"
     expires_after="2024-09-01">
   <owner>sammiequon@chromium.org</owner>
@@ -5401,8 +5296,8 @@
   <summary>
     Tracks the time from when a specific educational nudge is shown to when it's
     interacted with. Starts measuring time when the nudge is shown and records
-    the Nudge catalog name in one of the time buckets available if the user
-    performs the nudge's suggested action.
+    the Nudge catalog name in one of the `TimeRange` buckets if the user
+    performs the nudge's suggested action. These buckets are all exclusive.
   </summary>
   <token key="TimeRange">
     <variant name="Within1h"/>
diff --git a/tools/metrics/histograms/metadata/ash_growth/enums.xml b/tools/metrics/histograms/metadata/ash_growth/enums.xml
new file mode 100644
index 0000000..a14f543c
--- /dev/null
+++ b/tools/metrics/histograms/metadata/ash_growth/enums.xml
@@ -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.
+-->
+
+<!--
+
+This file describes the enumerations referenced by entries in histograms.xml for
+this directory. Some enums may instead be listed in the central enums.xml file
+at src/tools/metrics/histograms/enums.xml when multiple files use them.
+
+For best practices on writing enumerations descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#Enum-Histograms
+
+Please follow the instructions in the OWNERS file in this directory to find a
+reviewer. If no OWNERS file exists, please consider signing up at
+go/reviewing-metrics (Googlers only), as all subdirectories are expected to
+have an OWNERS file. As a last resort you can send the CL to
+chromium-metrics-reviews@google.com.
+-->
+
+<histogram-configuration>
+
+<!-- Enum types -->
+
+<enums>
+
+<enum name="CampaignSlot">
+  <int value="0" label="Demo Mode App"/>
+  <int value="1" label="Demo Mode Free Play Apps"/>
+  <int value="2" label="Nudge"/>
+  <int value="3" label="Notification"/>
+</enum>
+
+</enums>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/ash_growth/histograms.xml b/tools/metrics/histograms/metadata/ash_growth/histograms.xml
new file mode 100644
index 0000000..df2740c
--- /dev/null
+++ b/tools/metrics/histograms/metadata/ash_growth/histograms.xml
@@ -0,0 +1,131 @@
+<!--
+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.
+-->
+
+<!--
+This file is used to generate a comprehensive list of Ash histograms
+along with a detailed description for each histogram.
+
+For best practices on writing histogram descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md
+
+Please follow the instructions in the OWNERS file in this directory to find a
+reviewer. If no OWNERS file exists, please consider signing up at
+go/reviewing-metrics (Googlers only), as all subdirectories are expected to
+have an OWNERS file. As a last resort you can send the CL to
+chromium-metrics-reviews@google.com.
+-->
+
+<histogram-configuration>
+
+<histograms>
+
+<variants name="CampaignButtonId">
+  <variant name="0" summary="Primary"/>
+  <variant name="1" summary="Secondary"/>
+  <variant name="2" summary="Others"/>
+</variants>
+
+<histogram name="Ash.Growth.CampaignsComponent.DownloadDurationInOobe"
+    units="ms" expires_after="2024-10-17">
+  <owner>llin@google.com</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Records the time (in ms) the Campaigns Manager takes to download the
+    campaigns component from Omaha during OOBE flow. Recorded when loading the
+    campaigns component in OOBE flow has completed. This metric was split from
+    Ash.Growth.CampaignsComponent.DownloadDuration in M124.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Growth.CampaignsComponent.DownloadDurationSessionStart"
+    units="ms" expires_after="2024-10-17">
+  <owner>llin@google.com</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Records the time (in ms) the Campaigns Manager takes to download the
+    campaigns component from Omaha during session start. Recorded when loading
+    the campaigns component at session start has completed. This metric was
+    split from Ash.Growth.CampaignsComponent.DownloadDuration in M124.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Growth.CampaignsComponent.ParseDuration" units="ms"
+    expires_after="2024-10-17">
+  <owner>llin@google.com</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Records the time (in ms) the Campaigns Manager takes to read and parsed (as
+    JSON) from disk. Recorded when a component is loaded into Campaign Matcher.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Growth.CampaignsManager.Error"
+    enum="CampaignsManagerError" expires_after="2024-10-17">
+  <owner>llin@google.com</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Tracks unexpected error while loading and matching campaigns. Recorded each
+    time an unexpected error occurs.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Growth.CampaignsManager.GetCampaignBySlot"
+    enum="CampaignSlot" expires_after="2024-10-17">
+  <owner>llin@google.com</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Tracks the slot of the fetched campaign. Recorded each time a campaign is
+    fetched.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Growth.CampaignsManager.MatchDuration" units="ms"
+    expires_after="2024-10-17">
+  <owner>llin@google.com</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Records the time (in ms) the Campaigns Manager takes to match a campaign
+    based on the targeting criteria. Recorded when fetching a campaign for a
+    particular slot completed.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Growth.Ui.ButtonPressed.Button{ButtonId}.Campaigns500"
+    units="int" expires_after="2025-03-01">
+  <owner>wutao@chromium.org</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Recorded when the button of {ButtonId} in the UI is pressed. The bucket is
+    indexed by the campaign_id. The campaign_id recorded in this histogram
+    should be less than 500.
+  </summary>
+  <token key="ButtonId" variants="CampaignButtonId"/>
+</histogram>
+
+<histogram name="Ash.Growth.Ui.Dismissed.Campaigns500" units="int"
+    expires_after="2025-03-01">
+  <owner>wutao@chromium.org</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Recorded when the UI is dismissed. The bucket is indexed by the campaign_id.
+    The campaign_id recorded in this histogram should be less than 500.
+  </summary>
+</histogram>
+
+<histogram name="Ash.Growth.Ui.Impression.Campaigns500" units="int"
+    expires_after="2025-03-01">
+  <owner>wutao@chromium.org</owner>
+  <owner>cros-growth@google.com</owner>
+  <summary>
+    Recorded when it is ready to log a UI impression. Normally this is called
+    right after a call to show the UI. The bucket is indexed by the campaign_id.
+    The campaign_id recorded in this histogram should be less than 500.
+  </summary>
+</histogram>
+
+</histograms>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index e4509caf..fef8d42 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -226,9 +226,9 @@
   <variant name="IPH_iOSDefaultBrowserVideoPromoTrigger"
       summary="(obsolete) notifying that the conditions to show the default
                browser video promo have been met."/>
-  <variant name="IPH_iOSDockingPromoFeature"
+  <variant name="IPH_iOSDockingPromo"
       summary="Showing the Docking Promo on iOS"/>
-  <variant name="IPH_iOSDockingPromoRemindMeLaterFeature"
+  <variant name="IPH_iOSDockingPromoRemindMeLater"
       summary="Showing the Docking Promo (Remind Me Later version) on iOS"/>
   <variant name="IPH_iOSHistoryOnOverflowMenuFeature"
       summary="history item on the overflow menu on iOS"/>
diff --git a/tools/metrics/histograms/metadata/history/enums.xml b/tools/metrics/histograms/metadata/history/enums.xml
index ca4f6a1..2cfd12a 100644
--- a/tools/metrics/histograms/metadata/history/enums.xml
+++ b/tools/metrics/histograms/metadata/history/enums.xml
@@ -153,6 +153,13 @@
   <int value="5" label="Journeys"/>
 </enum>
 
+<enum name="HistoryResultType">
+  <int value="0"
+      label="Traditional history search result (reverse chronological list)"/>
+  <int value="1" label="Grouped history search result"/>
+  <int value="2" label="Embeddings search result"/>
+</enum>
+
 <enum name="OtherSessionsActions">
   <int value="0" label="Menu initialized"/>
   <int value="1" label="Menu shown (deprecated)"/>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 9a2d243..523dc57 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -2024,6 +2024,49 @@
   </summary>
 </histogram>
 
+<histogram name="History.SearchResultClicked.Index{HistoryResultType}"
+    units="index" expires_after="2025-04-15">
+  <owner>tommycli@chromium.org</owner>
+  <owner>orinj@chromium.org</owner>
+  <summary>
+    Logs the zero-based index of the result clicked.
+
+    For Embeddings and Traditional history types, this logs the index of the
+    visit with respect to visits of the same type only. For example, the first
+    matching Traditional visit has index 0, even though it's displayed below all
+    Embeddings matches.
+
+    For Grouped history, this logs the index of the visit WITHIN its containing
+    cluster. This is to match the History.Clusters.UIActions UMAs.
+
+    This does not log anything shown directly in the omnibox suggest surface.
+
+    This UMA is further sliced into per-type slices, and the sum of those
+    histograms equals the unsliced histogram. The maximum index is 99, and
+    clicks above that are clamped to that maximum.
+  </summary>
+  <token key="HistoryResultType">
+    <variant name="" summary="Clicks of all types, unsliced"/>
+    <variant name=".Embeddings" summary="Embeddings search result"/>
+    <variant name=".Grouped"
+        summary="Grouped history search result (index within cluster)"/>
+    <variant name=".Traditional"
+        summary="Traditional history search result (reverse chronological
+                 list)"/>
+  </token>
+</histogram>
+
+<histogram name="History.SearchResultClicked.Type" enum="HistoryResultType"
+    expires_after="2025-04-15">
+  <owner>tommycli@chromium.org</owner>
+  <owner>orinj@chromium.org</owner>
+  <summary>
+    Logs the type of the History result clicked from History UI surfaces. Note,
+    this does NOT include any results clicked from the omnibox suggest surface,
+    even if they are History or @history site search suggestions.
+  </summary>
+</histogram>
+
 <histogram name="History.TopSites.QueryFromHistoryTime" units="ms"
     expires_after="2024-09-15">
   <owner>mahmadi@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/settings/histograms.xml b/tools/metrics/histograms/metadata/settings/histograms.xml
index 6cb41ef1..b840548e 100644
--- a/tools/metrics/histograms/metadata/settings/histograms.xml
+++ b/tools/metrics/histograms/metadata/settings/histograms.xml
@@ -104,6 +104,17 @@
   </summary>
 </histogram>
 
+<histogram name="Settings.FingerprintingProtection.Enabled"
+    enum="BooleanEnabled" expires_after="2024-12-01">
+  <owner>fmacintosh@google.com</owner>
+  <owner>koilos@google.com</owner>
+  <summary>
+    Records the state of the pref that represents whether the user has chosen to
+    enable Fingerprinting Protection via the Tracking Protection settings page.
+    Logged on profile startup.
+  </summary>
+</histogram>
+
 <histogram name="Settings.FirstPartySets.State" enum="FirstPartySetsState"
     expires_after="2024-09-29">
   <owner>alimariam@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/sync/enums.xml b/tools/metrics/histograms/metadata/sync/enums.xml
index 3a55a76..bb365100 100644
--- a/tools/metrics/histograms/metadata/sync/enums.xml
+++ b/tools/metrics/histograms/metadata/sync/enums.xml
@@ -240,6 +240,7 @@
   <int value="69" label="AutofillPaymentCardBenefits"/>
   <int value="70" label="(obsolete) CloseTabs"/>
   <int value="71" label="ShowTabGroupsInBookmarkBar"/>
+  <int value="72" label="FacilitatedPaymentsPix"/>
   <int value="100000" label="AppLanguagePromptShown"/>
   <int value="100001" label="(Obsolete) PrefExplicitLanguageAskShown"/>
   <int value="100002" label="ContextualSearchEnabled_Android"/>
diff --git a/tools/metrics/histograms/metadata/uma/histograms.xml b/tools/metrics/histograms/metadata/uma/histograms.xml
index 83d08fd..c16665a 100644
--- a/tools/metrics/histograms/metadata/uma/histograms.xml
+++ b/tools/metrics/histograms/metadata/uma/histograms.xml
@@ -29,6 +29,8 @@
   <variant name="GpuMetrics" summary="For GPU process metrics."/>
   <variant name="NotificationHelperMetrics"
       summary="For notification_helper process metrics."/>
+  <variant name="PlatformExperienceHelperMetrics"
+      summary="For metrics from Platform Experience Helper"/>
   <variant name="PpapiBrokerMetrics"
       summary="For &quot;PPAPI broker&quot; process metrics."/>
   <variant name="PpapiPluginMetrics"
@@ -322,6 +324,7 @@
   <token key="Source">
     <variant name="DeferredBrowserMetrics"/>
     <variant name="NotificationHelperMetrics"/>
+    <variant name="PlatformExperienceHelperMetrics"/>
     <variant name="SetupMetrics"/>
   </token>
 </histogram>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 05dcb732..ad4d5a4 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "win": {
             "hash": "0e7ce4cd3c50b2701c0d06407af74aff6c31e1b6",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/6579c4e89c719f2501c5bb5baf20f137c0c35bd4/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/f6656e9828f5a5a5358d0782bd4d5cdecdc652f5/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "d8e27d961be1db97db098c6826017aec0397ecfd",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v44.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "a0badec3e42334e3ae0063342cf7e7317c2ef05e",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/f6656e9828f5a5a5358d0782bd4d5cdecdc652f5/trace_processor_shell"
+            "hash": "46f4391cc3ac746ec2ee3fc395eabc19c80dca29",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/234fd02711c642ec5211da272283db8fd8d91af4/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/shard_maps/linux-perf_map.json b/tools/perf/core/shard_maps/linux-perf_map.json
index 3cf18b5..ce8a2ca1 100644
--- a/tools/perf/core/shard_maps/linux-perf_map.json
+++ b/tools/perf/core/shard_maps/linux-perf_map.json
@@ -5,7 +5,7 @@
                 "abridged": false
             },
             "blink_perf.accessibility": {
-                "end": 12,
+                "end": 13,
                 "abridged": false
             },
             "jetstream2": {
@@ -31,6 +31,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -63,7 +66,7 @@
     "1": {
         "benchmarks": {
             "blink_perf.accessibility": {
-                "begin": 12,
+                "begin": 13,
                 "abridged": false
             },
             "blink_perf.bindings": {
@@ -93,6 +96,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -149,6 +155,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -182,7 +191,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 22,
+                "end": 21,
                 "abridged": false
             },
             "jetstream2": {
@@ -208,6 +217,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -231,8 +243,8 @@
     "4": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 22,
-                "end": 106,
+                "begin": 21,
+                "end": 105,
                 "abridged": false
             },
             "jetstream2": {
@@ -258,6 +270,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -281,7 +296,7 @@
     "5": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 106,
+                "begin": 105,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -297,7 +312,7 @@
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
-                "end": 20,
+                "end": 18,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -317,6 +332,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -340,7 +358,7 @@
     "6": {
         "benchmarks": {
             "blink_perf.shadow_dom": {
-                "begin": 20,
+                "begin": 18,
                 "abridged": false
             },
             "blink_perf.svg": {
@@ -379,6 +397,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -418,7 +439,7 @@
                 "abridged": false
             },
             "media.desktop": {
-                "end": 7,
+                "end": 6,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -438,6 +459,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -466,11 +490,11 @@
     "8": {
         "benchmarks": {
             "media.desktop": {
-                "begin": 7,
+                "begin": 6,
                 "abridged": false
             },
             "memory.desktop": {
-                "end": 2,
+                "end": 1,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -490,6 +514,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -513,8 +540,8 @@
     "9": {
         "benchmarks": {
             "memory.desktop": {
-                "begin": 2,
-                "end": 7,
+                "begin": 1,
+                "end": 6,
                 "abridged": false
             },
             "system_health.common_desktop": {
@@ -534,6 +561,9 @@
                 ],
                 "abridged": false
             },
+            "speedometer": {
+                "abridged": false
+            },
             "speedometer2": {
                 "abridged": false
             },
@@ -557,7 +587,7 @@
     "10": {
         "benchmarks": {
             "memory.desktop": {
-                "begin": 7,
+                "begin": 6,
                 "abridged": false
             },
             "octane": {
@@ -567,7 +597,10 @@
                 "abridged": false
             },
             "power.desktop": {
-                "end": 8,
+                "end": 3,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -607,14 +640,17 @@
     "11": {
         "benchmarks": {
             "power.desktop": {
-                "begin": 8,
+                "begin": 3,
                 "abridged": false
             },
             "rasterize_and_record_micro.top_25": {
                 "abridged": false
             },
             "rendering.desktop": {
-                "end": 24,
+                "end": 17,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -640,8 +676,11 @@
     "12": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 24,
-                "end": 77,
+                "begin": 17,
+                "end": 67,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -667,8 +706,11 @@
     "13": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 77,
-                "end": 135,
+                "begin": 67,
+                "end": 123,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -694,8 +736,11 @@
     "14": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 135,
-                "end": 181,
+                "begin": 123,
+                "end": 174,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -721,8 +766,11 @@
     "15": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 181,
-                "end": 232,
+                "begin": 174,
+                "end": 220,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -748,8 +796,11 @@
     "16": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 232,
-                "end": 279,
+                "begin": 220,
+                "end": 268,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -775,8 +826,11 @@
     "17": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 279,
-                "end": 325,
+                "begin": 268,
+                "end": 315,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -802,7 +856,7 @@
     "18": {
         "benchmarks": {
             "rendering.desktop": {
-                "begin": 325,
+                "begin": 315,
                 "abridged": false
             },
             "rendering.desktop.notracing": {
@@ -839,7 +893,7 @@
                 "abridged": false
             },
             "system_health.common_desktop": {
-                "end": 14,
+                "end": 6,
                 "abridged": false
             }
         }
@@ -847,8 +901,11 @@
     "19": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 14,
-                "end": 62,
+                "begin": 6,
+                "end": 54,
+                "abridged": false
+            },
+            "speedometer": {
                 "abridged": false
             },
             "speedometer2": {
@@ -874,11 +931,11 @@
     "20": {
         "benchmarks": {
             "system_health.common_desktop": {
-                "begin": 62,
+                "begin": 54,
                 "abridged": false
             },
             "system_health.memory_desktop": {
-                "end": 15,
+                "end": 14,
                 "abridged": false
             }
         }
@@ -886,8 +943,8 @@
     "21": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 15,
-                "end": 30,
+                "begin": 14,
+                "end": 27,
                 "abridged": false
             }
         }
@@ -895,8 +952,8 @@
     "22": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 30,
-                "end": 56,
+                "begin": 27,
+                "end": 54,
                 "abridged": false
             }
         }
@@ -904,8 +961,8 @@
     "23": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 56,
-                "end": 76,
+                "begin": 54,
+                "end": 75,
                 "abridged": false
             }
         }
@@ -913,11 +970,11 @@
     "24": {
         "benchmarks": {
             "system_health.memory_desktop": {
-                "begin": 76,
+                "begin": 75,
                 "abridged": false
             },
             "v8.browsing_desktop": {
-                "end": 25,
+                "end": 23,
                 "abridged": false
             }
         },
@@ -930,7 +987,7 @@
     "25": {
         "benchmarks": {
             "v8.browsing_desktop": {
-                "begin": 25,
+                "begin": 23,
                 "abridged": false
             },
             "v8.browsing_desktop-future": {
@@ -945,36 +1002,36 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1306,
-        "predicted_min_shard_time": 1173.0,
-        "predicted_min_shard_index": 6,
-        "predicted_max_shard_time": 1543.0,
+        "num_stories": 1326,
+        "predicted_min_shard_time": 1199.0,
+        "predicted_min_shard_index": 8,
+        "predicted_max_shard_time": 1647.0,
         "predicted_max_shard_index": 25,
-        "shard #0": 1249.0,
-        "shard #1": 1256.0,
-        "shard #2": 1256.0,
-        "shard #3": 1257.0,
-        "shard #4": 1257.0,
-        "shard #5": 1261.0,
-        "shard #6": 1173.0,
-        "shard #7": 1272.0,
-        "shard #8": 1327.0,
-        "shard #9": 1255.0,
-        "shard #10": 1259.0,
-        "shard #11": 1249.0,
-        "shard #12": 1264.0,
-        "shard #13": 1262.0,
-        "shard #14": 1267.0,
-        "shard #15": 1258.0,
-        "shard #16": 1245.0,
-        "shard #17": 1263.0,
-        "shard #18": 1247.0,
-        "shard #19": 1263.0,
-        "shard #20": 1229.0,
-        "shard #21": 1248.0,
-        "shard #22": 1284.0,
-        "shard #23": 1338.0,
-        "shard #24": 1239.0,
-        "shard #25": 1543.0
+        "shard #0": 1281.0,
+        "shard #1": 1278.0,
+        "shard #2": 1278.0,
+        "shard #3": 1275.0,
+        "shard #4": 1274.0,
+        "shard #5": 1272.0,
+        "shard #6": 1215.0,
+        "shard #7": 1264.0,
+        "shard #8": 1199.0,
+        "shard #9": 1287.0,
+        "shard #10": 1264.0,
+        "shard #11": 1282.0,
+        "shard #12": 1281.0,
+        "shard #13": 1287.0,
+        "shard #14": 1271.0,
+        "shard #15": 1278.0,
+        "shard #16": 1281.0,
+        "shard #17": 1283.0,
+        "shard #18": 1281.0,
+        "shard #19": 1294.0,
+        "shard #20": 1231.0,
+        "shard #21": 1269.0,
+        "shard #22": 1302.0,
+        "shard #23": 1275.0,
+        "shard #24": 1300.0,
+        "shard #25": 1647.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/cross_device_test_config.py b/tools/perf/cross_device_test_config.py
index ed34852..f506fc1 100644
--- a/tools/perf/cross_device_test_config.py
+++ b/tools/perf/cross_device_test_config.py
@@ -123,6 +123,11 @@
             'long_running:tools:gmail-background': 10,
             'browse:media:youtubetv:2019': 10
         },
+        # set speedometer to 20 shards to help warm up speedometer2
+        # benchmark runs on linux-perf b/325578543
+        'speedometer': {
+            'http://browserbench.org/Speedometer/': 20,
+        },
         'speedometer2': {
             'Speedometer2': 20,
         },
diff --git a/tools/polymer/html_to_wrapper.gni b/tools/polymer/html_to_wrapper.gni
index 894a4db..44a80d54d 100644
--- a/tools/polymer/html_to_wrapper.gni
+++ b/tools/polymer/html_to_wrapper.gni
@@ -47,7 +47,8 @@
         # Exclude non-web component "*_icons.html" or "icons.html" files.
         is_icons_template =
             get_path_info(html_file, "file") == "icons.html" ||
-            string_replace(html_file, "_icons.html", "") != html_file
+            string_replace(html_file, "_icons.html", "") != html_file ||
+            string_replace(html_file, "icons_lit.html", "") != html_file
         if (!is_icons_template) {
           inputs += [ "$in_folder/" +
                       string_replace(html_file, ".html", wrapper_extension) ]
diff --git a/tools/polymer/html_to_wrapper.py b/tools/polymer/html_to_wrapper.py
index c32f109..05eaa9b 100644
--- a/tools/polymer/html_to_wrapper.py
+++ b/tools/polymer/html_to_wrapper.py
@@ -61,6 +61,14 @@
 document.head.appendChild(template.content);
 """
 
+# Template for Lit icon HTML files.
+_LIT_ICONS_TEMPLATE = """import '%(scheme)s//resources/cr_elements/cr_icon/cr_iconset.js';
+import {html, render} from '%(scheme)s//resources/lit/v3_0/lit.rollup.js';
+
+const iconsetHtml = html`%(content)s`;
+render(iconsetHtml, document.head);
+"""
+
 # Tokens used to detect whether the underlying custom element is based on
 # Polymer or Lit.
 POLYMER_TOKEN = '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'
@@ -69,6 +77,7 @@
 # Map holding all the different types of HTML files to generate wrappers for.
 TEMPLATE_MAP = {
     'lit': _LIT_ELEMENT_TEMPLATE,
+    'lit_icons': _LIT_ICONS_TEMPLATE,
     'native': _NATIVE_ELEMENT_TEMPLATE,
     'polymer_icons': _POLYMER_ICONS_TEMPLATE,
     'polymer': _POLYMER_ELEMENT_TEMPLATE,
@@ -87,6 +96,16 @@
     return 'native'
 
 
+def detect_icon_template_type(icons_file):
+  with io.open(icons_file, encoding='utf-8', mode='r') as f:
+    content = f.read()
+    if 'iron-iconset-svg' in content:
+      return 'polymer_icons'
+    assert 'cr-iconset' in content, \
+        'icons files must include iron-iconset-svg or cr-iconset'
+    return 'lit_icons'
+
+
 _IMPORTS_START_REGEX = '^<!-- #html_wrapper_imports_start$'
 _IMPORTS_END_REGEX = '^#html_wrapper_imports_end -->$'
 
@@ -179,31 +198,52 @@
   # Wrap the input files (minified or not) with an enclosing .ts file.
   for in_file in args.in_files:
     wrapper_in_file = path.join(wrapper_in_folder, in_file)
+    template = None
+    template_type = args.template
+    filename = path.basename(in_file)
+    effective_in_file = wrapper_in_file
 
-    with io.open(wrapper_in_file, encoding='utf-8', mode='r') as f:
-      html_content = f.read()
-
-      template = None
-      template_type = args.template
-      filename = path.basename(in_file)
-      if filename == 'icons.html' or filename.endswith('_icons.html'):
-        assert args.template == 'polymer' or args.template == 'detect', (
-            r'Polymer icons files not supported with template="%s"' %
-            args.template)
+    if filename == 'icons.html' or filename.endswith('_icons.html'):
+      if args.template == 'polymer':
         template_type = 'polymer_icons'
-      elif template_type == 'detect':
-        # Locate the file that holds the web component's definition. Assumed to
-        # be in the same folder as input HTML template file.
-        definition_file = path.splitext(path.join(in_folder,
-                                                  in_file))[0] + extension
-        template_type = detect_template_type(definition_file)
+      elif args.template == 'lit':
+        template_type = 'lit_icons'
+      else:
+        assert args.template == 'detect', (
+            r'Polymer/Lit icons files not supported with template="%s"' %
+            args.template)
+        template_type = detect_icon_template_type(wrapper_in_file)
+    elif filename.endswith('icons_lit.html'):
+      assert args.template == 'lit' or args.template == 'detect', (
+          r'Lit icons files not supported with template="%s"' % args.template)
+      # Grab the content from the equivalent Polymer file, and substitute
+      # cr-iconset for iron-iconset-svg.
+      polymer_file = path.join(wrapper_in_folder,
+                               in_file.replace('icons_lit', 'icons'))
+      effective_in_file = polymer_file
+      template_type = 'lit_icons'
+    elif template_type == 'detect':
+      # Locate the file that holds the web component's definition. Assumed to
+      # be in the same folder as input HTML template file.
+      definition_file = path.splitext(path.join(in_folder,
+                                                in_file))[0] + extension
+      template_type = detect_template_type(definition_file)
+
+    with io.open(effective_in_file, encoding='utf-8', mode='r') as f:
+      html_content = f.read()
 
       substitutions = {
           'content': html_content,
           'scheme': 'chrome:' if args.scheme == 'chrome' else '',
       }
 
-      if template_type == 'lit':
+      if template_type == 'lit_icons':
+        # Replace iron-iconset-svg for the case of Lit icons files generated
+        # from a Polymer icons file.
+        if 'iron-iconset-svg' in html_content:
+          html_content = html_content.replace('iron-iconset-svg', 'cr-iconset')
+        substitutions['content'] = html_content
+      elif template_type == 'lit':
         # Add Lit specific substitutions.
         basename = path.splitext(path.basename(in_file))[0]
         # Derive class name from file name. For example
diff --git a/tools/polymer/html_to_wrapper_test.py b/tools/polymer/html_to_wrapper_test.py
index fc4fce1..1885fa7 100755
--- a/tools/polymer/html_to_wrapper_test.py
+++ b/tools/polymer/html_to_wrapper_test.py
@@ -107,6 +107,24 @@
                    'html_to_wrapper/icons.html.ts',
                    'html_to_wrapper/expected/icons.html.ts')
 
+  def testHtmlToWrapperIconsLit(self):
+    self._run_test('html_to_wrapper/cr_icons.html',
+                   'html_to_wrapper/cr_icons.html.ts',
+                   'html_to_wrapper/expected/cr_icons.html.ts',
+                   template='lit')
+
+  def testHtmlToWrapperIconsLit_Detect(self):
+    self._run_test('html_to_wrapper/cr_icons.html',
+                   'html_to_wrapper/cr_icons.html.ts',
+                   'html_to_wrapper/expected/cr_icons.html.ts',
+                   template='detect')
+
+  def testHtmlToWrapperLitFromPolymer(self):
+    self._run_test('html_to_wrapper/icons_lit.html',
+                   'html_to_wrapper/icons_lit.html.ts',
+                   'html_to_wrapper/expected/cr_icons.html.ts',
+                   template='detect')
+
   def testHtmlToWrapper_Minify(self):
     self._run_test('html_to_wrapper/foo.html',
                    'html_to_wrapper/foo.html.ts',
diff --git a/tools/polymer/tests/html_to_wrapper/cr_icons.html b/tools/polymer/tests/html_to_wrapper/cr_icons.html
new file mode 100644
index 0000000..45ec5ea
--- /dev/null
+++ b/tools/polymer/tests/html_to_wrapper/cr_icons.html
@@ -0,0 +1,7 @@
+<cr-iconset name="dummy" size="24">
+  <svg>
+    <defs>
+      <g id="foo"><path d="M12 2C6.48 2 2 6.48 2"></path></g>
+    </defs>
+  </svg>
+</cr-iconset>
diff --git a/tools/polymer/tests/html_to_wrapper/expected/cr_icons.html.ts b/tools/polymer/tests/html_to_wrapper/expected/cr_icons.html.ts
new file mode 100644
index 0000000..b97fe1a
--- /dev/null
+++ b/tools/polymer/tests/html_to_wrapper/expected/cr_icons.html.ts
@@ -0,0 +1,12 @@
+import '//resources/cr_elements/cr_icon/cr_iconset.js';
+import {html, render} from '//resources/lit/v3_0/lit.rollup.js';
+
+const iconsetHtml = html`<cr-iconset name="dummy" size="24">
+  <svg>
+    <defs>
+      <g id="foo"><path d="M12 2C6.48 2 2 6.48 2"></path></g>
+    </defs>
+  </svg>
+</cr-iconset>
+`;
+render(iconsetHtml, document.head);
diff --git a/tools/polymer/tests/html_to_wrapper/icons_lit.html b/tools/polymer/tests/html_to_wrapper/icons_lit.html
new file mode 100644
index 0000000..de34e14
--- /dev/null
+++ b/tools/polymer/tests/html_to_wrapper/icons_lit.html
@@ -0,0 +1,4 @@
+<!--
+ Purposefully empty since this file is generated at buildtime from the
+ Polymer version.
+-->
diff --git a/tools/run-swarmed.py b/tools/run-swarmed.py
index c6041aa..e678ad8 100755
--- a/tools/run-swarmed.py
+++ b/tools/run-swarmed.py
@@ -125,12 +125,19 @@
       raise Exception('Either both of --ios-sim-version and --ios-sim-platform '
                       'or --ios-device is required')
 
-    trigger_args.extend(['-service-account', args.service_account])
     trigger_args.extend(
         ['-named-cache', f'xcode_ios_{args.ios_xcode_build_version}=Xcode.app'])
     trigger_args.extend(
         ['-cipd-package', '.:infra/tools/mac_toolchain/${platform}=latest'])
 
+  if args.service_account:
+    account = args.service_account
+  elif args.swarming_instance == 'chromium-swarm':
+    account = 'chromium-tester@chops-service-accounts.iam.gserviceaccount.com'
+  elif args.swarming_instance == 'chrome-swarming':
+    account = 'chrome-tester@chops-service-accounts.iam.gserviceaccount.com'
+  trigger_args.extend(['-service-account', account])
+
   if args.arch != 'detect':
     trigger_args += [
         '-d',
@@ -271,11 +278,11 @@
                            '--system-log-file flags to the comment.')
   parser.add_argument('out_dir', type=str, help='Build directory.')
   parser.add_argument('target_name', type=str, help='Name of target to run.')
-  # ios only args
   parser.add_argument(
       '--service-account',
-      default='chromium-tester@chops-service-accounts.iam.gserviceaccount.com',
-      help='The service account that the swarming task will be run using')
+      help='Optional service account that the swarming task will be run using. '
+      'Default value will be set based on the "--swarming-instance".')
+  # ios only args
   parser.add_argument('--ios-xcode-build-version',
                       help='The version of xcode that will be used for all '
                       'xcodebuild CLI commands')
diff --git a/tools/typescript/path_mappings.py b/tools/typescript/path_mappings.py
index 9750d19..20be8b40 100644
--- a/tools/typescript/path_mappings.py
+++ b/tools/typescript/path_mappings.py
@@ -26,6 +26,7 @@
       "cr_components/customize_color_scheme_mode",
       "cr_components/customize_themes",
       "cr_components/help_bubble",
+      "cr_components/history",
       "cr_components/history_embeddings",
       "cr_components/history_clusters",
       "cr_components/localized_link",
diff --git a/tools/utr/builders.py b/tools/utr/builders.py
index be75280..e066a88 100644
--- a/tools/utr/builders.py
+++ b/tools/utr/builders.py
@@ -9,7 +9,8 @@
 
 _THIS_DIR = pathlib.Path(__file__).resolve().parent
 _SRC_DIR = _THIS_DIR.parents[1]
-# TODO(crbug.com/41492688): Support src-internal configs too.
+# TODO(crbug.com/41492688): Support src-internal configs too. When this is done,
+# ensure tools/utr/recipe.py is not using the public reclient instance
 _BUILDER_PROP_DIRS = _SRC_DIR.joinpath('infra', 'config', 'generated',
                                        'builders')
 
diff --git a/tools/utr/recipe.py b/tools/utr/recipe.py
index 1687a62..6494a92 100644
--- a/tools/utr/recipe.py
+++ b/tools/utr/recipe.py
@@ -148,6 +148,15 @@
             },
         },
     }
+    # TODO(crbug.com/41492688): Use the chrome version for internal builders
+    # when they are added.
+    # Set reclient and siso to use untrusted even for imitating ci builders
+    if not '$build/reclient' in input_props:
+      input_props['$build/reclient'] = {}
+    input_props['$build/reclient']['instance'] = 'rbe-chromium-untrusted'
+    if not '$build/siso' in input_props:
+      input_props['$build/siso'] = {}
+    input_props['$build/siso']['project'] = 'rbe-chromium-untrusted'
     self._input_props = input_props
 
   def _run(self, adapter, rerun_props=None):
diff --git a/tools/web_dev_style/js_checker.py b/tools/web_dev_style/js_checker.py
index 1bda403..4ee72c2 100644
--- a/tools/web_dev_style/js_checker.py
+++ b/tools/web_dev_style/js_checker.py
@@ -120,7 +120,7 @@
     affected_files = self.input_api.AffectedFiles(file_filter=self.file_filter,
                                                   include_deletes=False)
     affected_js_files = [
-        f for f in affected_files if f.LocalPath().endswith((".js", "ts"))
+        f for f in affected_files if f.LocalPath().endswith((".js", ".ts"))
     ]
 
     if affected_js_files:
@@ -134,7 +134,6 @@
             _f for _f in [
                 self.BindThisCheck(i, line),
                 self.ChromeSendCheck(i, line),
-                self.CommentIfAndIncludeCheck(i, line),
                 self.EndJsDocCommentCheck(i, line),
                 self.ExtraDotInGenericCheck(i, line),
                 self.InheritDocCheck(i, line),
@@ -143,6 +142,16 @@
             ] if _f
         ]
 
+      if not f.LocalPath().endswith((".html.js", ".html.ts")):
+        # Exclude JS/TS files holding HTML strings from
+        # CommentIfAndIncludeCheck().
+        for i, line in f.ChangedContents():
+          error_lines += [
+              _f for _f in [
+                  self.CommentIfAndIncludeCheck(i, line),
+              ] if _f
+          ]
+
       if error_lines:
         error_lines = [
             "Found JavaScript style violations in %s:" %
diff --git a/ui/chromeos/strings/network/network_element_localized_strings_provider.cc b/ui/chromeos/strings/network/network_element_localized_strings_provider.cc
index daf1191..aa8bced 100644
--- a/ui/chromeos/strings/network/network_element_localized_strings_provider.cc
+++ b/ui/chromeos/strings/network/network_element_localized_strings_provider.cc
@@ -551,6 +551,8 @@
                           ash::features::IsApnRevampEnabled());
   html_source->AddBoolean("isCellularCarrierLockEnabled",
                           ash::features::IsCellularCarrierLockEnabled());
+  html_source->AddBoolean("isApnPoliciesEnabled",
+                          ash::features::IsApnPoliciesEnabled());
 
   html_source->AddString("apnSettingsDescriptionWithLink",
                          l10n_util::GetStringFUTF16(
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 45d8e75..c6565792 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -2392,43 +2392,17 @@
 
 void DisplayManager::InsertAndUpdateDisplayInfo(
     const ManagedDisplayInfo& new_info) {
-  ManagedDisplayInfo* info = nullptr;
   auto it = display_info_.find(new_info.id());
   if (it != display_info_.end()) {
-    info = &(it->second);
+    ManagedDisplayInfo* info = &(it->second);
     info->Copy(new_info);
   } else {
-    info = &display_info_[new_info.id()];
-    *info = new_info;
-
+    display_info_[new_info.id()] = new_info;
     // Set from_native_platform to false so that all information
     // (rotation, zoom factor etc.) is copied.
-    info->set_from_native_platform(false);
-
-    // If an external display is plugged in for the first time and doesn't have
-    // any entry in display_info_, such as those from Pref or from previous
-    // config, apply recommended default zoom factor.
-    ApplyDefaultZoomFactorIfNecessary(*info);
+    display_info_[new_info.id()].set_from_native_platform(false);
   }
-
-  CHECK(info);
-  info->UpdateDisplaySize();
-}
-
-void DisplayManager::ApplyDefaultZoomFactorIfNecessary(
-    ManagedDisplayInfo& info) {
-  // Only apply to external display. The internal display has good handle of
-  // default dpi.
-  if (IsInternalDisplayId(info.id())) {
-    return;
-  }
-
-  // Ignore unified display.
-  if (info.id() == kUnifiedDisplayId) {
-    return;
-  }
-
-  info.UpdateZoomFactorToMatchTargetDPI();
+  display_info_[new_info.id()].UpdateDisplaySize();
 }
 
 Display DisplayManager::CreateDisplayFromDisplayInfoById(int64_t id) {
diff --git a/ui/display/manager/display_manager.h b/ui/display/manager/display_manager.h
index 2686341..139d45d 100644
--- a/ui/display/manager/display_manager.h
+++ b/ui/display/manager/display_manager.h
@@ -601,11 +601,6 @@
   // |GetDisplayInfo| to get the correct ManagedDisplayInfo for a display.
   void InsertAndUpdateDisplayInfo(const ManagedDisplayInfo& new_info);
 
-  // Applies recommended zoom factor when necessary, only used when an external
-  // display is connected for the first time. e.g. when a 4K native mode is used
-  // when firstly connected, the content is almost certainly too small.
-  void ApplyDefaultZoomFactorIfNecessary(ManagedDisplayInfo& info);
-
   // Creates a display object from the ManagedDisplayInfo for
   // |display_id|.
   Display CreateDisplayFromDisplayInfoById(int64_t display_id);
diff --git a/ui/display/manager/managed_display_info.cc b/ui/display/manager/managed_display_info.cc
index c4f026b..db1e98e 100644
--- a/ui/display/manager/managed_display_info.cc
+++ b/ui/display/manager/managed_display_info.cc
@@ -39,13 +39,6 @@
 
 const float kDpi96 = 96.0;
 
-// The recommended default external display DPI, only used when an external
-// display is connected for the first time. e.g. when a 4K native mode is used
-// when firstly connected, the content is almost certainly too small. The value
-// comes from the metrics of currently most used external effective display DPI
-// - Ash.Display.ExternalDisplay.ActiveEffectiveDPI.
-const float kRecommendedDefaultExternalDisplayDpi = kDpi96;
-
 // Check the content of |spec| and fill |bounds| and |device_scale_factor|.
 // Returns true when |bounds| is found.
 void GetDisplayBounds(const std::string& spec,
@@ -506,53 +499,6 @@
   return pixel_size / static_cast<float>(logical_size);
 }
 
-void ManagedDisplayInfo::UpdateZoomFactorToMatchTargetDPI() {
-  // Only update zoom factor if device dpi is valid.
-  if (!device_dpi_) {
-    return;
-  }
-
-  const float target_zoom_factor =
-      device_dpi_ / kRecommendedDefaultExternalDisplayDpi;
-
-  // Refine zoom factor based on available zoom factors in settings.
-  const int display_larger_side =
-      std::max(bounds_in_native_.width(), bounds_in_native_.height());
-  const std::vector<float> avaialble_zoom_factors =
-      GetDisplayZoomFactorsByDisplayWidth(display_larger_side);
-  DCHECK_GE(avaialble_zoom_factors.size(), 1u);
-
-  const float min_zoom_factor = avaialble_zoom_factors.front();
-  const float max_zoom_factor = avaialble_zoom_factors.back();
-  // Check min boundary.
-  if (target_zoom_factor <= min_zoom_factor) {
-    zoom_factor_ = min_zoom_factor;
-  } else if (target_zoom_factor >= max_zoom_factor) {
-    // Check max boundary.
-    zoom_factor_ = max_zoom_factor;
-  } else {
-    // Round to the neareast available zoom factor.
-    DCHECK(std::is_sorted(avaialble_zoom_factors.begin(),
-                          avaialble_zoom_factors.end()));
-    for (size_t i = 0; i < avaialble_zoom_factors.size() - 1; i++) {
-      const float left_bound = avaialble_zoom_factors[i];
-      const float right_bound = avaialble_zoom_factors[i + 1];
-      if (target_zoom_factor >= right_bound) {
-        continue;
-      }
-
-      zoom_factor_ =
-          (target_zoom_factor - left_bound < right_bound - target_zoom_factor)
-              ? left_bound
-              : right_bound;
-      break;
-    }
-  }
-
-  // Also update the zoom factor in the zoom_factor_map_.
-  AddZoomFactorForSize(size_in_pixel_.ToString(), zoom_factor_);
-}
-
 gfx::Size ManagedDisplayInfo::GetSizeInPixelWithPanelOrientation() const {
   gfx::Size size = bounds_in_native_.size();
   if (panel_orientation_ == display::PanelOrientation::kLeftUp ||
diff --git a/ui/display/manager/managed_display_info.h b/ui/display/manager/managed_display_info.h
index 89bb5e6..a4951bf2 100644
--- a/ui/display/manager/managed_display_info.h
+++ b/ui/display/manager/managed_display_info.h
@@ -258,10 +258,6 @@
   // TODO(oshima): Rename to |GetDeviceScaleFactor()|.
   float GetEffectiveDeviceScaleFactor() const;
 
-  // Updates the zoom factor so that the effective dpi matches to the
-  // recommended default dpi.
-  void UpdateZoomFactorToMatchTargetDPI();
-
   // Copy the display info except for fields that can be modified by a user
   // (|rotation_|). |rotation_| is copied when the |another_info| isn't native
   // one.
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index f3b0ff3..5ad5e831 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -725,6 +725,7 @@
     "test/scoped_default_font_description.h",
     "test/sk_color_eq.cc",
     "test/sk_color_eq.h",
+    "test/sk_gmock_support.h",
   ]
   if (is_apple) {
     sources += [ "image/image_unittest_util_apple.mm" ]
diff --git a/ui/gfx/DEPS b/ui/gfx/DEPS
index 0ddd58a..a7eb37a 100644
--- a/ui/gfx/DEPS
+++ b/ui/gfx/DEPS
@@ -13,8 +13,6 @@
   "+ui/base/ui_base_features.h",
   "+ui/ios",
   "+ui/linux",
-
-  "-testing/gmock",
 ]
 
 specific_include_rules = {
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
index 6b71fcd4..030ae391 100644
--- a/ui/gfx/color_transform.cc
+++ b/ui/gfx/color_transform.cc
@@ -1134,8 +1134,7 @@
     steps_.push_back(std::move(src_range_adjust_matrix));
 
   if (src.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
-    // BT2020 CL is a special case.
-    steps_.push_back(std::make_unique<ColorTransformFromBT2020CL>());
+    NOTREACHED_NORETURN();
   } else {
     steps_.push_back(std::make_unique<ColorTransformMatrix>(
         Invert(src.GetTransferMatrix(options.src_bit_depth))));
@@ -1182,9 +1181,7 @@
   }
 
   if (src.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
-    // BT2020 CL is a special case.
-    steps_.push_back(std::make_unique<ColorTransformMatrix>(
-        Invert(src.GetTransferMatrix(options.src_bit_depth))));
+    NOTREACHED_NORETURN();
   }
   steps_.push_back(
       std::make_unique<ColorTransformMatrix>(src.GetPrimaryMatrix()));
@@ -1259,9 +1256,7 @@
   steps_.push_back(
       std::make_unique<ColorTransformMatrix>(Invert(dst.GetPrimaryMatrix())));
   if (dst.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
-    // BT2020 CL is a special case.
-    steps_.push_back(std::make_unique<ColorTransformMatrix>(
-        dst.GetTransferMatrix(options.dst_bit_depth)));
+    NOTREACHED_NORETURN();
   }
 
   switch (dst.GetTransferID()) {
@@ -1313,7 +1308,7 @@
     steps_.push_back(std::move(dst_range_adjust_matrix));
 
   if (dst.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
-    NOTREACHED();
+    NOTREACHED_NORETURN();
   } else {
     steps_.push_back(std::make_unique<ColorTransformMatrix>(
         dst.GetTransferMatrix(options.dst_bit_depth)));
diff --git a/ui/gfx/color_transform_unittest.cc b/ui/gfx/color_transform_unittest.cc
index fa113b5..7b5f65f 100644
--- a/ui/gfx/color_transform_unittest.cc
+++ b/ui/gfx/color_transform_unittest.cc
@@ -90,35 +90,6 @@
   EXPECT_GT(tmp.z(), tmp.y());
 }
 
-TEST(SimpleColorSpace, BT2020CLtoBT2020RGB) {
-  ColorSpace bt2020cl(
-      ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::BT2020_10,
-      ColorSpace::MatrixID::BT2020_CL, ColorSpace::RangeID::LIMITED);
-  ColorSpace bt2020rgb(ColorSpace::PrimaryID::BT2020,
-                       ColorSpace::TransferID::BT2020_10,
-                       ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL);
-  std::unique_ptr<ColorTransform> t(
-      ColorTransform::NewColorTransform(bt2020cl, bt2020rgb));
-
-  ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f);
-  t->Transform(&tmp, 1);
-  EXPECT_NEAR(tmp.x(), 0.0f, kMathEpsilon);
-  EXPECT_NEAR(tmp.y(), 0.0f, kMathEpsilon);
-  EXPECT_NEAR(tmp.z(), 0.0f, kMathEpsilon);
-
-  tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f);
-  t->Transform(&tmp, 1);
-  EXPECT_NEAR(tmp.x(), 1.0f, kMathEpsilon);
-  EXPECT_NEAR(tmp.y(), 1.0f, kMathEpsilon);
-  EXPECT_NEAR(tmp.z(), 1.0f, kMathEpsilon);
-
-  // Test a blue color
-  tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f);
-  t->Transform(&tmp, 1);
-  EXPECT_GT(tmp.z(), tmp.x());
-  EXPECT_GT(tmp.z(), tmp.y());
-}
-
 TEST(SimpleColorSpace, YCOCGLimitedToSRGB) {
   ColorSpace ycocg(ColorSpace::PrimaryID::BT709, ColorSpace::TransferID::SRGB,
                    ColorSpace::MatrixID::YCOCG, ColorSpace::RangeID::LIMITED);
diff --git a/ui/gfx/test/sk_gmock_support.h b/ui/gfx/test/sk_gmock_support.h
new file mode 100644
index 0000000..0c91f35
--- /dev/null
+++ b/ui/gfx/test/sk_gmock_support.h
@@ -0,0 +1,75 @@
+// 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_GFX_TEST_SK_GMOCK_SUPPORT_H_
+#define UI_GFX_TEST_SK_GMOCK_SUPPORT_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/test/sk_color_eq.h"
+
+namespace gfx::test {
+
+MATCHER_P2(IsCloseToBitmap, expected_bmp, max_per_channel_deviation, "") {
+  // Number of pixels with an error
+  int error_pixels_count = 0;
+
+  gfx::Rect error_bounding_rect = gfx::Rect();
+
+  // Check that bitmaps have identical dimensions.
+  if (arg.width() != expected_bmp.width()) {
+    *result_listener << "where widths do not match, actual: " << arg.width()
+                     << ", expected: " << expected_bmp.width();
+    return false;
+  }
+  if (arg.height() != expected_bmp.height()) {
+    *result_listener << "where heights do not match, actual: " << arg.height()
+                     << ", expected: " << expected_bmp.height();
+    return false;
+  }
+
+  for (int x = 0; x < arg.width(); ++x) {
+    for (int y = 0; y < arg.height(); ++y) {
+      SkColor actual_color = arg.getColor(x, y);
+      SkColor expected_color = expected_bmp.getColor(x, y);
+      if (!ColorsClose(actual_color, expected_color,
+                       max_per_channel_deviation)) {
+        ++error_pixels_count;
+        error_bounding_rect.Union(gfx::Rect(x, y, 1, 1));
+      }
+    }
+  }
+
+  if (error_pixels_count != 0) {
+    *result_listener
+        << "Number of pixel with an error, given max_per_channel_deviation of "
+        << max_per_channel_deviation << ": " << error_pixels_count
+        << "\nError Bounding Box : " << error_bounding_rect.ToString() << "\n";
+    int sample_x = error_bounding_rect.x();
+    int sample_y = error_bounding_rect.y();
+    std::string expected_color = color_utils::SkColorToRgbaString(
+        expected_bmp.getColor(sample_x, sample_y));
+    std::string actual_color =
+        color_utils::SkColorToRgbaString(arg.getColor(sample_x, sample_y));
+    *result_listener << "Sample pixel comparison at " << sample_x << "x"
+                     << sample_y << ": Expected " << expected_color
+                     << ", actual " << actual_color;
+    return false;
+  }
+
+  return true;
+}
+
+MATCHER_P(EqualsBitmap, expected_bmp, "") {
+  return testing::ExplainMatchResult(
+      IsCloseToBitmap(expected_bmp,
+                      /*max_per_channel_deviation=*/0),
+      arg, result_listener);
+}
+
+}  // namespace gfx::test
+
+#endif  // UI_GFX_TEST_SK_GMOCK_SUPPORT_H_
diff --git a/ui/views/accessibility/view_accessibility.cc b/ui/views/accessibility/view_accessibility.cc
index 7c0ee61d..3489dc5 100644
--- a/ui/views/accessibility/view_accessibility.cc
+++ b/ui/views/accessibility/view_accessibility.cc
@@ -239,19 +239,6 @@
 
   views::ViewAccessibilityUtils::Merge(/*source*/ data_, /*destination*/ *data);
 
-  // The ignored state depends on more than just the kIgnored state of the data,
-  // for instance it also depends on if the view has been pruned from the tree.
-  // And since some of those states we keep track of in member variables, we
-  // need to add this check here at the end so that if those states were set, we
-  // add the kIgnored state to the final AXNodeData.
-  // TODO(accessibility): We'll eventually want to replace this with a more
-  // robust and less ambiguous system, such as what Blink does on the render
-  // side. We might need something like ComputeIsHidden(), which could try to
-  // mimic what Blink does when computing 'ignoredness' of a node.
-  if (ViewAccessibility::GetIsIgnored()) {
-    data->AddState(ax::mojom::State::kIgnored);
-  }
-
   // This was previously found earlier in the function. It has been moved here,
   // after the call to `ViewAccessibility::Merge`, so that we only check the
   // `data` after all the attributes have been set. Otherwise, there was a bug
@@ -420,6 +407,7 @@
   }
 
   data_.role = role;
+  AdjustIgnoredState();
 }
 
 void ViewAccessibility::SetRole(const ax::mojom::Role role,
@@ -642,22 +630,18 @@
 }
 
 void ViewAccessibility::SetIsIgnored(bool is_ignored) {
-  if (is_ignored == data_.IsIgnored()) {
+  if (is_ignored == should_be_ignored_) {
     return;
   }
 
-  if (is_ignored) {
-    data_.AddState(ax::mojom::State::kIgnored);
-  } else {
-    data_.RemoveState(ax::mojom::State::kIgnored);
-  }
+  should_be_ignored_ = is_ignored;
 
+  AdjustIgnoredState();
   view_->NotifyAccessibilityEvent(ax::mojom::Event::kTreeChanged, true);
 }
 
 bool ViewAccessibility::GetIsIgnored() const {
-  return data_.HasState(ax::mojom::State::kIgnored) ||
-         ViewAccessibility::IsChildOfLeaf() || GetIsPruned();
+  return data_.HasState(ax::mojom::State::kIgnored);
 }
 
 void ViewAccessibility::OverrideNativeWindowTitle(const std::string& title) {
@@ -796,6 +780,7 @@
   internal::ScopedChildrenLock lock(view_);
   for (auto& child : view_->children()) {
     child->GetViewAccessibility().pruned_ = true;
+    child->GetViewAccessibility().AdjustIgnoredState();
     child->GetViewAccessibility().PruneSubtree();
   }
 
@@ -808,7 +793,7 @@
   internal::ScopedChildrenLock lock(view_);
   for (auto& child : view_->children()) {
     child->GetViewAccessibility().pruned_ = false;
-
+    child->GetViewAccessibility().AdjustIgnoredState();
     // If we encounter a node that has already been explicitly set to be a leaf,
     // don't unprune it/its subtree. Otherwise we could end up in situations
     // where we have a node that is set to be a leaf, but has unpruned children.
@@ -822,4 +807,18 @@
     child->UnpruneVirtualSubtree();
   }
 }
+
+void ViewAccessibility::SetState(ax::mojom::State state, bool is_enabled) {
+  if (is_enabled) {
+    data_.AddState(state);
+  } else {
+    data_.RemoveState(state);
+  }
+}
+
+void ViewAccessibility::AdjustIgnoredState() {
+  bool is_ignored =
+      should_be_ignored_ || pruned_ || data_.role == ax::mojom::Role::kNone;
+  SetState(ax::mojom::State::kIgnored, is_ignored);
+}
 }  // namespace views
diff --git a/ui/views/accessibility/view_accessibility.h b/ui/views/accessibility/view_accessibility.h
index a04ff4a..e2570b9e 100644
--- a/ui/views/accessibility/view_accessibility.h
+++ b/ui/views/accessibility/view_accessibility.h
@@ -215,7 +215,9 @@
 
   void SetIsSelected(bool selected);
 
-  // Hides this view from the accessibility APIs.
+  // Hides this view from the accessibility APIs. Keep in mind that this is not
+  // the sole determinant of whether the ignored state is set. See
+  // `AdjustIgnoredState`.
   void SetIsIgnored(bool is_ignored);
   virtual bool GetIsIgnored() const;
 
@@ -413,6 +415,12 @@
 
   bool pruned_ = false;
 
+  // This is set to true when the view is explicitly marked as ignored by
+  // `SetIsIgnored`. It is not the only condition that will cause a view to have
+  // the ignored accessible state, as `pruned_` and `is_leaf_` can also cause
+  // this. See `AdjustIgnoredState`.
+  bool should_be_ignored_ = false;
+
   // Used by the Views system to help some assistive technologies, such as
   // screen readers, transition focus from one widget to another.
   base::WeakPtr<Widget> next_focus_ = nullptr;
@@ -441,6 +449,10 @@
   void PruneSubtree();
   void UnpruneSubtree();
 
+  void SetState(ax::mojom::State state, bool is_enabled);
+
+  void AdjustIgnoredState();
+
   bool ignore_missing_widget_for_testing_ = false;
 };
 
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc
index e15e955..bc62bc2d 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -10,6 +10,7 @@
 
 #include "base/check_op.h"
 #include "base/i18n/rtl.h"
+#include "base/notreached.h"
 #include "build/build_config.h"
 #include "cc/paint/paint_flags.h"
 #include "third_party/skia/include/core/SkPath.h"
@@ -131,19 +132,17 @@
 }
 
 gfx::Size TabbedPaneTab::CalculatePreferredSize() const {
-  int width = preferred_title_width_ + GetInsets().width();
-  if (tabbed_pane_->GetStyle() == TabbedPane::TabStripStyle::kHighlight &&
-      tabbed_pane_->GetOrientation() == TabbedPane::Orientation::kVertical)
-    width = std::max(width, 192);
-  return gfx::Size(width, 32);
+  NOTREACHED_NORETURN() << "Use CalculatePreferredSize(SizeBounds)";
 }
 
-int TabbedPaneTab::GetHeightForWidth(int w) const {
-  // Because we set the LayoutManager, it will use
-  // LayoutManager::GetPreferredHeightForWidth by default, but this is not
-  // consistent with the fixed height desired by CalculatePreferredSize, so we
-  // override it and call it manually.
-  return CalculatePreferredSize().height();
+gfx::Size TabbedPaneTab::CalculatePreferredSize(
+    const SizeBounds& available_size) const {
+  int width = preferred_title_width_ + GetInsets().width();
+  if (tabbed_pane_->GetStyle() == TabbedPane::TabStripStyle::kHighlight &&
+      tabbed_pane_->GetOrientation() == TabbedPane::Orientation::kVertical) {
+    width = std::max(width, 192);
+  }
+  return gfx::Size(width, 32);
 }
 
 void TabbedPaneTab::GetAccessibleNodeData(ui::AXNodeData* data) {
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.h b/ui/views/controls/tabbed_pane/tabbed_pane.h
index 48f164c..3e3337a0 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.h
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.h
@@ -176,8 +176,9 @@
   void OnMouseEntered(const ui::MouseEvent& event) override;
   void OnMouseExited(const ui::MouseEvent& event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
-  gfx::Size CalculatePreferredSize() const override;
-  int GetHeightForWidth(int w) const override;
+  gfx::Size CalculatePreferredSize() const final;
+  gfx::Size CalculatePreferredSize(
+      const SizeBounds& available_size) const override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   bool HandleAccessibleAction(const ui::AXActionData& action_data) override;
   void OnFocus() override;
diff --git a/ui/views/examples/multiline_example.cc b/ui/views/examples/multiline_example.cc
index af9f8a9..c0c3b890 100644
--- a/ui/views/examples/multiline_example.cc
+++ b/ui/views/examples/multiline_example.cc
@@ -86,28 +86,27 @@
     render_text_->Draw(canvas);
   }
 
-  gfx::Size CalculatePreferredSize() const override {
-    // Turn off multiline mode to get the single-line text size, which is the
-    // preferred size for this view.
-    render_text_->SetMultiline(false);
-    gfx::Size size(render_text_->GetContentWidth(),
-                   render_text_->GetStringSize().height());
-    size.Enlarge(GetInsets().width(), GetInsets().height());
-    render_text_->SetMultiline(true);
-    return size;
-  }
+  gfx::Size CalculatePreferredSize(
+      const SizeBounds& available_size) const override {
+    int w = available_size.width().value_or(0);
+    if (w == 0) {
+      // Turn off multiline mode to get the single-line text size, which is the
+      // preferred size for this view.
+      render_text_->SetMultiline(false);
+      gfx::Size size(render_text_->GetContentWidth(),
+                     render_text_->GetStringSize().height());
+      size.Enlarge(GetInsets().width(), GetInsets().height());
+      render_text_->SetMultiline(true);
+      return size;
+    }
 
-  int GetHeightForWidth(int w) const override {
-    // TODO(ckocagil): Why does this happen?
-    if (w == 0)
-      return View::GetHeightForWidth(w);
     const gfx::Rect old_rect = render_text_->display_rect();
     gfx::Rect rect = old_rect;
     rect.set_width(w - GetInsets().width());
     render_text_->SetDisplayRect(rect);
     int height = render_text_->GetStringSize().height() + GetInsets().height();
     render_text_->SetDisplayRect(old_rect);
-    return height;
+    return gfx::Size(w, height);
   }
 
   void OnThemeChanged() override {
diff --git a/ui/views/layout/layout_types.h b/ui/views/layout/layout_types.h
index 14e05150..152dc37 100644
--- a/ui/views/layout/layout_types.h
+++ b/ui/views/layout/layout_types.h
@@ -62,6 +62,10 @@
     return is_bounded() ? std::min(this->value(), value) : value;
   }
 
+  constexpr int value_or(int defaule_value) const {
+    return is_bounded() ? value() : defaule_value;
+  }
+
   void operator+=(const SizeBound& rhs);
   void operator-=(const SizeBound& rhs);
 
diff --git a/ui/views/test/test_views.cc b/ui/views/test/test_views.cc
index c5dd24a8..69ac753 100644
--- a/ui/views/test/test_views.cc
+++ b/ui/views/test/test_views.cc
@@ -44,14 +44,16 @@
   PreferredSizeChanged();
 }
 
-int ProportionallySizedView::GetHeightForWidth(int w) const {
-  return w * factor_;
-}
-
-gfx::Size ProportionallySizedView::CalculatePreferredSize() const {
-  if (preferred_width_ >= 0)
-    return gfx::Size(preferred_width_, GetHeightForWidth(preferred_width_));
-  return View::CalculatePreferredSize();
+gfx::Size ProportionallySizedView::CalculatePreferredSize(
+    const SizeBounds& available_size) const {
+  if (available_size.width().is_bounded()) {
+    int w = available_size.width().value();
+    return gfx::Size(w, w * factor_);
+  } else if (preferred_width_ >= 0) {
+    return gfx::Size(preferred_width_, preferred_width_ * factor_);
+  } else {
+    return View::CalculatePreferredSize(available_size);
+  }
 }
 
 BEGIN_METADATA(ProportionallySizedView)
diff --git a/ui/views/test/test_views.h b/ui/views/test/test_views.h
index dfed1df..5f65e72 100644
--- a/ui/views/test/test_views.h
+++ b/ui/views/test/test_views.h
@@ -61,8 +61,8 @@
 
   void SetPreferredWidth(int width);
 
-  int GetHeightForWidth(int w) const override;
-  gfx::Size CalculatePreferredSize() const override;
+  gfx::Size CalculatePreferredSize(
+      const SizeBounds& available_size) const override;
 
  private:
   // The multiplicative factor between width and height, i.e.
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 1954443..0694d559 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -91,12 +91,14 @@
 
     if (!is_android) {
       public_deps += [
+        "cr_components/history:build_grdp",
         "cr_components/history_clusters:build_grdp",
         "cr_components/history_embeddings:build_grdp",
         "cr_components/searchbox:build_grdp",
         "cr_components/searchbox/icons:build_grdp",
       ]
       grdp_files += [
+        "$target_gen_dir/cr_components/history/resources.grdp",
         "$target_gen_dir/cr_components/history_clusters/resources.grdp",
         "$target_gen_dir/cr_components/history_embeddings/resources.grdp",
         "$target_gen_dir/cr_components/searchbox/resources.grdp",
diff --git a/ui/webui/resources/cr_components/history/BUILD.gn b/ui/webui/resources/cr_components/history/BUILD.gn
new file mode 100644
index 0000000..fb5adc71
--- /dev/null
+++ b/ui/webui/resources/cr_components/history/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//ui/webui/resources/tools/build_webui.gni")
+
+assert(!is_android && !is_ios)
+
+build_webui("build") {
+  grd_prefix = "cr_components_history_embeddings"
+  non_web_component_files = [ "constants.ts" ]
+
+  ts_out_dir = "$root_gen_dir/ui/webui/resources/tsc/cr_components/history"
+  ts_composite = true
+  generate_grdp = true
+  grd_resource_path_prefix = rebase_path(".", "//ui/webui/resources")
+}
diff --git a/ui/webui/resources/cr_components/history/OWNERS b/ui/webui/resources/cr_components/history/OWNERS
new file mode 100644
index 0000000..fc5441da1
--- /dev/null
+++ b/ui/webui/resources/cr_components/history/OWNERS
@@ -0,0 +1,4 @@
+file://components/history_embeddings/OWNERS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ui/webui/resources/cr_components/history/constants.ts b/ui/webui/resources/cr_components/history/constants.ts
new file mode 100644
index 0000000..c464e89
--- /dev/null
+++ b/ui/webui/resources/cr_components/history/constants.ts
@@ -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.
+
+/**
+ * Histogram buckets for UMA tracking of which type of result the History user
+ * clicked.
+ */
+export enum HistoryResultType {
+  TRADITIONAL = 0,
+  GROUPED = 1,
+  EMBEDDINGS = 2,
+  END = 3,
+}
diff --git a/ui/webui/resources/cr_components/history_clusters/BUILD.gn b/ui/webui/resources/cr_components/history_clusters/BUILD.gn
index 3a25340..5bd216f 100644
--- a/ui/webui/resources/cr_components/history_clusters/BUILD.gn
+++ b/ui/webui/resources/cr_components/history_clusters/BUILD.gn
@@ -64,6 +64,7 @@
     "../page_image_service:build_ts",
     "//third_party/lit/v3_0:build_ts",
     "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources/cr_components/history:build_ts",
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
diff --git a/ui/webui/resources/cr_components/history_clusters/cluster.ts b/ui/webui/resources/cr_components/history_clusters/cluster.ts
index 46228f6..a0b8f409 100644
--- a/ui/webui/resources/cr_components/history_clusters/cluster.ts
+++ b/ui/webui/resources/cr_components/history_clusters/cluster.ts
@@ -12,6 +12,7 @@
 import '//resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 import '//resources/cr_elements/cr_auto_img/cr_auto_img.js';
 
+import {HistoryResultType} from '//resources/cr_components/history/constants.js';
 import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js';
 import {assert} from '//resources/js/assert.js';
 import {loadTimeData} from '//resources/js/load_time_data.js';
@@ -186,9 +187,18 @@
         ClusterAction.kVisitClicked, this.index);
 
     const visit = event.detail;
+    const visitIndex = this.getVisitIndex_(visit);
     MetricsProxyImpl.getInstance().recordVisitAction(
-        VisitAction.kClicked, this.getVisitIndex_(visit),
-        MetricsProxyImpl.getVisitType(visit));
+        VisitAction.kClicked, visitIndex, MetricsProxyImpl.getVisitType(visit));
+
+    this.dispatchEvent(new CustomEvent('record-history-link-click', {
+      bubbles: true,
+      composed: true,
+      detail: {
+        resultType: HistoryResultType.GROUPED,
+        index: visitIndex,
+      },
+    }));
   }
 
   private onOpenAllVisits_() {
diff --git a/ui/webui/resources/cr_components/history_embeddings/BUILD.gn b/ui/webui/resources/cr_components/history_embeddings/BUILD.gn
index 9cefa67e..937a1cc 100644
--- a/ui/webui/resources/cr_components/history_embeddings/BUILD.gn
+++ b/ui/webui/resources/cr_components/history_embeddings/BUILD.gn
@@ -33,6 +33,7 @@
   ts_composite = true
   ts_deps = [
     "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources/cr_components/history:build_ts",
     "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
diff --git a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html
index 6054f54..c3536dd 100644
--- a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html
+++ b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.html
@@ -94,7 +94,8 @@
       <template is="dom-repeat" items="[[searchResult_.items]]">
         <cr-url-list-item url="[[item.url.url]]" title="[[item.title]]"
             description="[[item.urlForDisplay]]"
-            on-click="onResultClick_" as-anchor always-show-suffix>
+            on-click="onResultClick_" on-auxclick="onResultClick_"
+            as-anchor always-show-suffix>
           <span slot="suffix">[[item.relativeTime]]</span>
           <cr-icon-button slot="suffix" iron-icon="cr:more-vert"
               on-click="onMoreActionsClick_">
diff --git a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts
index a4af990..9668d15 100644
--- a/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts
+++ b/ui/webui/resources/cr_components/history_embeddings/history_embeddings.ts
@@ -10,6 +10,7 @@
 import '//resources/cr_elements/cr_shared_vars.css.js';
 import '//resources/cr_elements/cr_url_list_item/cr_url_list_item.js';
 
+import {HistoryResultType} from '//resources/cr_components/history/constants.js';
 import type {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import type {CrLazyRenderElement} from '//resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js';
@@ -125,6 +126,15 @@
 
   private onResultClick_(e: DomRepeatEvent<SearchResultItem>) {
     this.dispatchEvent(new CustomEvent('result-click', {detail: e.model.item}));
+
+    this.dispatchEvent(new CustomEvent('record-history-link-click', {
+      bubbles: true,
+      composed: true,
+      detail: {
+        resultType: HistoryResultType.EMBEDDINGS,
+        index: e.model.index,
+      },
+    }));
   }
 
   private onSearchQueryChanged_() {
diff --git a/ui/webui/resources/cr_elements/cr_icon/iconset_map.ts b/ui/webui/resources/cr_elements/cr_icon/iconset_map.ts
index 91e3733b8c..df89445 100644
--- a/ui/webui/resources/cr_elements/cr_icon/iconset_map.ts
+++ b/ui/webui/resources/cr_elements/cr_icon/iconset_map.ts
@@ -20,18 +20,27 @@
 window.addEventListener('iron-iconset-added', e => {
   const event = e as unknown as CustomEvent<Iconset>;
   const map = IconsetMap.getInstance();
-  map.set(event.detail.name, event.detail);
+  map.setIronIconset(event.detail.name, event.detail);
 });
 
 export class IconsetMap extends EventTarget {
   private iconsets_: Map<string, Iconset> = new Map();
+  private ironIconsets_: Map<string, Iconset> = new Map();
 
   static getInstance() {
     return iconsetMap || (iconsetMap = new IconsetMap());
   }
 
   get(id: string): Iconset|null {
-    return this.iconsets_.get(id) || null;
+    return this.iconsets_.get(id) || this.ironIconsets_.get(id) || null;
+  }
+
+  // Remove this method once iron-iconset is no longer used.
+  setIronIconset(id: string, iconset: Iconset) {
+    assert(!this.ironIconsets_.has(id),
+           'Tried to add a second iron-iconset with id ' + id);
+    this.ironIconsets_.set(id, iconset);
+    this.dispatchEvent(new CustomEvent('cr-iconset-added', {detail: id}));
   }
 
   set(id: string, iconset: Iconset) {
diff --git a/v8 b/v8
index 628dd19..3d675d1a 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 628dd1909b2405aa0a68f9bd1aa00bbe5bc717b0
+Subproject commit 3d675d1ac3f96a60508bffa312e48bd46ddb466f